Repository navigation
Add FF/REW tap seeking and independent playback navigation options - #921
Conversation
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
…ansport Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
|
Thanks! I think we should pick a different name than "legacy". It's unclear what it means, and the project is only a few months old and still in beta. |
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
|
Fair enough, I went with "legacy" thinking of Plex's original controls, but I agree, in the context of this repo it's confusing. Maybe a name like "classic" would work here? |
|
Could you add a few screenshots to the PR description? The HUD row with Previous/Next (focused and disabled), a hidden-HUD seek, and a visible-HUD seek with the pending target would do. Simulator + mock PMS is fine. |
|
Sure. I’ve added four simulator + mock PMS screenshots to the PR description, showing Previous/Next focused and disabled, seeking with the HUD hidden, and a pending seek with the HUD visible. Please let me know if you’d like anything else |
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
GLinnik21
left a comment
There was a problem hiding this comment.
Thanks for this, and for the screenshots. I'd like to merge it, with a different split of the seeking behaviour and some changes to the buttons.
Seeking: move it to the remote's rewind / fast-forward keys and drop the setting
The current D-pad behaviour with the controls hidden is deliberate. The first Left/Right shows the controls, the next moves the target, and OK confirms. That's YouTube's model on TV and I'd like to keep it. The dedicated rewind / fast-forward keys are where an instant jump fits, so:
- No setting. Remove Classic Playback Controls: the preference, its locale shards and the settings row.
- D-pad Left/Right: unchanged from
main. Revert them to today's reveal → target → OK. - Rewind / fast-forward, tapped: jump straight away. Jump by the skip interval with the controls staying hidden. This is your current hidden-seek path, keyed on FF/REW instead of Left/Right, including
SeekBurst, so quick taps add up from the last target. Please keep the long-GOP regression and point it at FF/REW. - Rewind / fast-forward, held: keep today's behaviour. The continuous scrub with OK to confirm. A tap jumps, a hold scrubs.
Previous / Next: always in the row, with these changes
- Show them by default whenever there's an episode either side in the play queue. Leave them out for movies and extras.
- Keep the order and add a wider gap after Next:
[Previous][Next], gap,[Subtitles][Audio][More]. Use the sharedHStack's per-item gap,Item::new(BTN_S).after(2.0 * CONTROL_GAP)on Next, rather than new layout code or spacing in the screen. - When there's nothing either side, use
Item::absent(). That keeps Subtitles/Audio/More exactly where they are today, since the row is right-anchored. Please add a layout test that their x positions match with and without the pair. This also folds the separate classic row (ITEMS/ACTIONS) back into the default one. - Dim the side that's unavailable. That's the first episode under 5 s, or the last episode. The current
INK_DISABLEDlook is fine. - A disabled button shouldn't be selectable. Today Left/Right step through every button whatever its state (the
nav.btnclamp inscreens/src/player/mod.rs), and Up parks on index 0 (primary_btn()returns 0), which is a disabled Previous at the start of a first episode.- Please skip disabled buttons.
- Land on Next when it's enabled, otherwise on the first enabled button.
- Use the shared focus mechanism rather than the hand-written clamp if you can.
- Add a test for moving focus along the row. The current tests only check
action().
- Focused + disabled draws a blank white disc.
TransportButtonpicksINK_DISABLEDwithout looking at focus, so 22% white lands on the near-white focused face. #9 makes it unreachable, but the widget shouldn't be able to draw it either. - Show the 5-second rule on the button. Past 5 s, draw Previous with the existing restart icon (
Icon::Restart,assets/icons/restart.svg) instead of the skip-back glyph. No text label.
Smaller things
- Check the title still fits beside the wider row at the longest title and largest text size (
title_right). - Rename the
classic_controlsmodules, the doc and the translation keys to match. Nothing is "classic" any more. Updatedocs/classic-playback-controls.mdto describe the FF/REW jump, the buttons and the queue window. - With Left/Right reverted, check which replay anchors still need the re-recording.
The queue-window work is the right approach, so please keep it as is: reading the existing PlayQueue around the item so Previous survives moving on, and sharing that handoff with Up Next.
Generated by Claude Code
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
…acy-playback-controls Signed-off-by: Stefan Ovezea <sovezea@gmail.com> # Conflicts: # rust-modules/plex/src/plex/timeline.rs # rust-modules/screens/src/player/mod.rs # rust-modules/screens/src/registry.rs # rust-modules/src/app/playback.rs # tests/fixtures/replay/1-boot-home-chip-grid/manifest.json # tests/fixtures/replay/1-boot-home-chip-grid/rec-0000.jsonl # tests/fixtures/replay/12-filmography-detail-return/manifest.json # tests/fixtures/replay/12-filmography-detail-return/rec-0000.jsonl # tests/fixtures/replay/6-settings-family/manifest.json # tests/fixtures/replay/6-settings-family/rec-0000.jsonl
Signed-off-by: Stefan Ovezea <sovezea@gmail.com>
|
Thanks for the detailed review! I’ve updated the seeking behavior, button layout, focus handling and restart icon, while keeping the queue-window work. I’ve also refreshed the screenshots and added focused regression tests. One compromise I’d like to propose: keeping Instant D-pad seeking as a separate option, disabled by default. My remote only has a D-pad, so FF/REW alone wouldn’t cover my use case. Instant jumps are especially useful when watching anime that mixes canon and filler material. The default reveal → target → OK behavior stays unchanged. |
The maintainer review asked for no toggle on the episode buttons, so the "Previous/Next buttons" preference is removed end to end: the session field and setting row, the live cell, the Settings picker, its locale strings in every shard, the boot scenario and the docs. The pair now shows whenever an episode has a neighbour in its PlayQueue. Instant D-pad seeking stays as the one opt-in playback option. Also drops the leftover "legacy"/"modern" wording and the empty ScrubPress::Row arm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xn4z8v7ojwK4Xm9cci5Qon
Behaviour-preserving cleanups from a reuse/simplification/efficiency review of the branch: - delete the now-uncalled request_play_up_next and disc_hit_rect; the show-title rule lives once in request_play_queue_item - InstantDpadSeeking is written like DeckPress (LADDER, soft deserializer beside its siblings) instead of a one-use macro - Controls::pair is derived; Controls::now skips the queue lookup and the playhead read for non-episodes - the player row maps its actions straight to overlays; one disc stack builder; one row_index decoder; end_jump_gesture/drop_transport_tap replace the copied resets - TransportButton rules out a focused disabled face once, at draw time - queue_navigation shares current_index and stores fetched windows without copying CI fixes on the tree merged with main: the focus probe prints the episode-pair keys for every row occupant, and the per-profile and Settings picker counts match the merged settings list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xn4z8v7ojwK4Xm9cci5Qon
|
Thanks @stefan-ovezea, merged! I kept Instant D-pad seeking as you proposed, and pushed a few finishing commits to your branch before merging:
Thanks again for the work and for turning the review around so fast. Generated by Claude Code |
Playback behavior
Dedicated FF/REW keys now distinguish tap from hold. A tap jumps by the configured Skip interval on release, keeps the HUD hidden and preserves pause. A hold uses the existing shared continuous scrub and waits for OK, without committing an initial jump.
SeekBurstaccumulates quick taps from the last requested target, so an earlier keyframe landing cannot erase the requested travel. The long-GOP regression now exercises FF with the D-pad option Off.D-pad Left/Right retains main's reveal → target → OK behavior by default. Settings → Video & playback now contains two independent per-profile options:
Why retain an opt-in D-pad option?
This is a proposed compromise with the request to remove the setting. Some compact TV remotes only have a D-pad and no dedicated FF/REW keys, so moving immediate jumps exclusively to transport keys makes them inaccessible on those remotes. An opt-in D-pad mode is useful when watching anime that mixes filler and canon material: viewers may make frequent jumps through unwanted sections, then use Next to move to the following episode. It does not detect or automatically skip filler.
The option starts Off, so viewers who never enable it keep the deliberate reveal → target → OK interaction. Separating it from the episode buttons also means neither preference forces the other behavior. The buttons start On to match the requested default visibility. All previous combined-setting names, code references and translation shards have been replaced with names describing these two options.
Episode row and queue continuity
[Previous][Next], a wider gap, then[Subtitles][Audio][More]. Next uses the shared HStack'sItem::new(BTN_S).after(2.0 * CONTROL_GAP); absent episode controls useItem::absent(). A layout regression verifies that the three existing discs have identical x positions with and without the pair.Icon::Restart. Before five seconds it plays the previous queue item, or is disabled on the first episode. Next plays the following item, or is disabled at the end.TransportButtonrejects focused+disabled styling in either builder order and at draw time, preventing a blank white disc even if a caller supplies inconsistent state.The existing queue-window approach is retained: handoffs read the current PlayQueue around the target using
center,window,includeBefore,includeAfterandown=0. They preserve the queue ID and preceding rows instead of POSTing a replacement tail. Up Next uses the same handoff. Failed or mismatched refreshes retain the known snapshot.Merged upstream main through
b835afb8and preserved its typed metadata runner and same-turn effect handling. A boundary regression also caught the application dispatcher discarding FF/REW identity by classifying those keys as ordinary focus arrows; transport keys now retain their original identity through classification. The requested-target change does not modify production decoding or the rebuffering engine.Validation
Focused checks ran on the clean, committed tree at
05aa02d9; the full local suite was intentionally not rerun.283 focused unit tests passed: player input, row geometry/actions/title fitting, disabled-widget styling, shared focus travel, independent preference persistence/defaults, queue navigation/window reads, Settings navigation and the application transport-key boundary. Representative exact results:
12 simulator scenarios passed against mock PMS/generated media: default D-pad reveal; optional hidden-HUD D-pad jumps; FF tap/hold separation; visible-HUD pending target/OK; five-second restart; Next→Previous queue continuity; and rapid FF taps over 12-second keyframes. The rapid-tap requested targets were
12.432s → 22.432s → 32.432s, while the native seek destinations were12s → 12s → 24s.The Home, Filmography and Settings replay anchors still needed re-recording because the canonical screen bundle now includes FF tap and seek-burst state. They were re-recorded using
plxnative-rec rerecord, retaining anchor flags, rather than editing hashes. All six Target/Resolve replays passed with zero differences; all three recordings pass the synthetic-data checker. The Settings capture includes an explicit settled return and stays within the existing recording-reader limit.make check-nodef, the ARM debug build (make FLAVOR=debug,tvdevprofile), and the macOS simulator build passed. Module layers, list caps, Plex request registration andcheck-deps.shpassed without expanding structural allowlists.A manual audit checked affected documentation claims. Public source and PR text were checked with the outbound guard.
This revision has simulator playback and ARM build evidence. It has not been exercised on the LG TV; the simulator does not establish LG decoding, video-plane composition, text rasterization or device performance.
Build benchmark
Ran
make build-bench ARGS='--only noop,leaf --runs 1'on the base and feature commits in this checkout, on the same Apple M1 Pro (10 cores), rustc 1.99.1, serial front end. This compiles tests without running the unit suite.b835afb805aa02d9The feature tree includes the five missing Russian entries. The benchmark restored a clean tree. This is a single sample with host-load warnings, and the baseline did not build, so it establishes no before/after speed claim.
Screenshots
Captured from the simulator's own 1920×1080 framebuffer at
05aa02d9, using mock PMS, synthetic titles and generated media. The clock sink and system-FFmpeg screenshot compositor demonstrate UI states only. These are not desktop screenshots or device performance measurements.Next focused; Previous disabled before five seconds
Previous focused; Next disabled on the final episode
Previous uses the restart icon after five seconds
FF tap seeks with the HUD hidden
Visible-HUD D-pad seek previews a pending target and waits for OK
Independent settings and their defaults (before choosing a profile)
Review notes
Behavior, maintenance seams, queue-window details and synthetic reproduction commands are in
docs/playback-navigation.md.