Conversation
Auth: - Login/logout with bcryptjs password hashing and express-session - First user created becomes admin; admin creates all other accounts - Setup flow shown on first visit when no users exist yet - Global fetch interceptor shows login overlay on 401 Access control: - Admin assigns top-level media folders per user via allowedPaths - /api/browse, /api/stream, /api/artwork, /api/metadata all require auth - Root browse filters to user's allowed dirs; sub-path access denied if not under an allowed path Cross-device state sync: - Device UUID stored in localStorage; state pushed to server every 5s while playing - On load: if another device was active within 30 min, offer to resume - On first play: if another device is active within 5 min, offer to take over - PUT /api/state stores queue, index, position, sort prefs, active device Custom playlists: - Create/rename/delete playlists; add tracks via long-press context menu - Play full playlist or jump to individual tracks - Stored per-user in data/playlists.json Admin panel: - Full-screen user management: create, edit (password/role/folders), delete - Folder picker uses /api/browse root listing for checkbox selection Data stored in data/ (gitignored); sessions persist via session-file-store Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cross-device sync: - Remove resume banner — state now syncs silently on login - Both devices auto-load same queue/track on connect (no prompt) - Takeover banner still shown when another device is active within 30 min - Takeover button added to fullscreen fs-extra row (shown when not active device) - Transport buttons (prev/play/next) on non-active device send remote commands to the server via POST /api/command; active device polls every 3s and executes - Active device pushes updated state after executing a command (clears it server-side) - When another device claims active status, local audio is paused automatically - Add POST /api/command server endpoint; PUT /api/state now always clears pendingCommand Admin panel: - Move '+ Add user' button above the user list so it's visible without scrolling Username display: - Truncate with ellipsis at 110px so long usernames don't overflow topbar on mobile Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n attribute Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… eye toggle Folder access: - Replace flat top-level checklist with a recursive lazy-loading folder tree - Each node has an expand (▶/▼) button that fetches subdirs on first click - Checking any folder grants access to it and all its subdirectories - Works for both create and edit forms Edit user section: - Separate 'Edit User' section with a proper h3 header - User dropdown selector — choose any user from the list to edit - Edit form appears below after selecting; dismiss with Cancel - Delete button remains in the 'All Users' list Password fields: - All password creation/change forms now have a confirm field - Passwords must match before the form can submit - Eye toggle button on every password field (click to show/hide) - Login page: eye toggle on password; confirm field shown only during setup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er button position - Add svg[hidden] override inside .pw-eye so only one eye icon shows at a time - Override flex-direction on .ftree-label inside admin forms so checkbox stays inline - Defer media src loading in restore() to prevent browser "Continue playing" prompt; seek to saved position on first play - Move fs-btn-takeover out of icon row into its own .fs-takeover row at the bottom of the fullscreen player Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck selection - Active device pushes position on every poll tick while playing so non-active devices see live progress without user interaction - Add syncPositionDisplay() to Player so non-active devices update seek bar and time display from server state on each poll - Add duration to server state (PUT /api/state) so non-active devices can convert seek-bar percentage to seconds without loading audio - Intercept skip ±30s buttons in capture phase to send skip commands when another device is active - Intercept seek bar change events to send seek commands to the active device - Add loadqueue command type (server + executeCommand) so non-active devices can queue tracks on the active device - Wrap Player.startQueue to redirect to active device via loadqueue command when another device is playing; show takeover banner so user can switch if desired - Fix takeoverHere() to update serverState.activeDeviceId before calling loadState so startQueue is not erroneously redirected back during takeover Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tive devices Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add getDuration(), getPosition(), seekTo() to Player so SyncManager always
uses the active media element (video or audio) rather than hardcoding audio
- Fix getPlayerState() to use Player.getDuration/getPosition so video position
and duration are correctly pushed to the server
- Fix executeCommand('seek') to use Player.seekTo() so seeks land on the video
element when a video is playing
- Fix seek bar interception to use serverState.duration (already correct for
video) instead of document.getElementById('audio').duration
- Rewrite syncQueue to be display-only: updates title/art/metadata without
loading any media stream, and exits video mode so the non-active device shows
a static artwork image instead of attempting to play the video
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e devices - Fix video unpausing then immediately re-pausing: active device was not pushing after executing a play command (only pushed when result was paused), so the next poll still saw the same pendingCommand and executed it again. Now always push 200ms after any command execution to clear it from the server. - Separate the position-push path (no pending command + playing) from the command-execution path so they can't interfere with each other. - Add playing boolean to server state and getPlayerState() so non-active devices know whether the active device is playing or paused. - Call Player.syncPlayState(state.playing) on each poll so the play/pause button icon stays in sync on non-active devices. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dden attribute Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.