Repository navigation
Split the video block into parts, and drive the real app in tests - #330
Merged
Merged
Conversation
The guards on its commands, the floating stacked layout, the delay texts, the reopen checks, SyncedView and the SponsorBlock seek mark each go where the rest of their kind already is, leaving video_block.py to the block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
cmd_active, is_active_runtime_param_set_to and the block's manual_seek, manual_view and manual_rate take a dotted name, "bookmarks.next", as well as a plain one, so a part of the block taken out into an object of its own keeps its commands without the block passing each of them on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
What is done with a video's bookmarks -- adding, jumping, the marker's menu, renaming, colouring, the manager -- goes to block.bookmarker, its commands reached as "bookmarker.add" and the like. The guards on the block's commands ask the block a part belongs to (VideoBlockPart), and the block keeps the bookmarks themselves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
Zooming, the zoom to a selection and its hint, following another video's view, crop, alignment and moving the picture go to block.view, their commands reached as "view.scale_increase" and the like. manual_view stays with the block, the others following from there as they do for seeks and speeds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
…arts The chapters, and the jumps and loops between them, go to block.chapters, the chapters themselves now block.chapters.all and their commands "chapters.next" and the like. What SponsorBlock found, what of it is marked and the skipping go to block.sponsorblock. The block keeps what the site said of the video, its YouTube ID and length, for both to go by, and puts the seek bar's marks together from them and the bookmarks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
The subtitle tracks, the choice among them, the files kept beside the video, the preferred languages, the encoding and the delay go to block.subtitles, their commands "subtitles.set_track" and the like. A stand-in for the block in tests is given its parts by mock_block_parts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
The audio tracks and the choice among them, the output devices, the preferred languages, the files kept beside the video, the channel mode and the delay go to block.audio, their commands "audio.set_track" and the like. A dub served as another stream is still the block's to follow, now as follow_audio_language, the audio part going to it for that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
…e into its audio
Screenshots are a ScreenshotControl at block.screenshot ("screenshot.take"),
setting the loop a LoopControl at block.loop ("loop.set_start",
"loop.toggle_random", ...), and volume and mute go to block.audio, which
already had everything else about the sound. The loop's bounds, loop_start
and loop_end, stay on the block with the seeking that reads them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
…coveryControl
Reloading a network video that failed, the countdown to it, and going on
past an item of the list that would not play are block.recovery, with the
settings for both ("recovery.set_retry_mode", "recovery.set_fail_action",
...). The block no longer reaches into its timers and counters: it asks
for retries, note_played(), stop(), stop_skip() and the like. Showing the
error itself stays with the block.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
…Control
Which rung of the quality ladder plays, fitting it to the pane, which dub
of a video served one language at a time, what the stream proxy is handed,
and reloading a live video every so often are block.stream
("stream.switch_quality", "stream.set_audio_language", ...). The proxy's
failure for the stream is asked of it too, as it knows the URL. The
ladders themselves, block.streams, stay where the resolver puts them.
Tests that borrowed the block's methods one by one now drive a real
StreamControl.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
…tusControl How far a video got loading, the error card and its Details window, and which face the overlay shows with them are block.status, along with what they keep: last_error, load_stage, load_steps, is_kept_for_picture. Failing itself (error, network_error) stays with the block, which hands the card the tries made. The parts now read the block's is_error and is_closing rather than its private fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
…o a NavigationControl
Going to the next, previous or a random video along the cell's list or
through its folder, taking up and leaving a list, its List window and the
place on it said over the video, and going on from a file that is gone are
block.navigation ("navigation.next", "navigation.leave_list", ...). Which
list and item the cell is on, media_list and list_item, stay with the
block. "playlist" already names the saved grid, hence not that.
Every part is now made before the URL resolver, which is wired to them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
…ment the block parts A misspelt "part.method" is no error until its menu item is chosen, and then the item just does nothing. The test reads the commands named for the active block, the ones let through while loading, the ones sent to all blocks and the signals that carry those to each block, and checks each one is there. AGENTS.md now says how the video block is split into parts and what to watch for when moving code into one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
…debug run can use tests/real_app starts the player offscreen with VLC, in a process of its own, and runs a scenario against it: open media, choose menu items, wait for the state to follow, check it, take screenshots. ffmpeg makes the media into build/test-media on first use (clips, a list, a file with chapters and audio and subtitle tracks and files beside it, a stream in three sizes and a dubbed one), and HLS is served from the run itself. The scenarios in tests/real_app/scenarios cover playback, folder and list stepping, stream sizes and a failing link, dubs, tracks, and chapters, bookmarks and SponsorBlock (answered locally). One shows a known bug, VLC moving the video back on a track change while an audio file plays beside it, and is expected to fail until it is fixed. They run with `just test-real-app` (pytest -m real_app) and as a CI job of their own; a plain pytest leaves them out. A one-off scenario file runs with `python -m tests.real_app.run`, which AGENTS.md describes along with what the driver takes care of. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
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.
Summary
gridplayer/widgets/video_block.pygoes from 6716 to 2211 lines. The block keeps its core (loading, playback, seeking, rate, Qt events); everything else moves into parts, oneVideoBlockPartper file, kept on the block as attributes:statusvideo_block_status.pybookmarkervideo_block_bookmarker.pyviewvideo_block_view.pychapters,sponsorblockvideo_block_chapters.py,video_block_sponsorblock.pysubtitles,audiovideo_block_subtitles.py,video_block_audio.pyloop,screenshotvideo_block_loop.py,video_block_screenshot.pyrecoveryvideo_block_recovery.pystreamvideo_block_stream.pynavigationvideo_block_navigation.py("active", "navigation.next");cmd_active,manual_seek/manual_view/manual_rateandLOADING_COMMANDSresolve them. Theonly_*guards work on part methods.tests/test_dotted_commands.pychecks that every command named by the menus, keys, dialogs andLOADING_COMMANDS, and everyall_*signal's slot, resolves to a method.Real-app tests
tests/real_appdrives the real player offscreen with VLC, through its menus, from scenarios:media.pymakes the media with ffmpeg intobuild/test-media/on first use (nothing binary in git).driver.py/run.py:uv run python -m tests.real_app.run <scenario or file.py>runs one and leaves a report, the app log and screenshots.external_audio_jumpis a known bug, marked xfail(strict): with an external audio file attached, choosing a subtitle track makes VLC move the video back to where the file was added. The app only sets the track; the same happens before this refactor.uv run pytestleaves them out (addopts = -m 'not real_app');just test-real-appruns them, and so does a newreal-appCI job, which uploads the runs' reports on failure.AGENTS.md documents the parts pattern and how to drive the real app.
Testing
uv run pytest: 6374 passed, 9 skipped.uv run pytest -m real_app: 6 passed, 1 xfailed, about 50s; repeated 9 times, three at a time, without a failure.🤖 Generated with Claude Code
https://claude.ai/code/session_01MTS355x5fJ6RENK1HLYqmX
Generated by Claude Code