Quality, stability, and performance overhaul - #208
Conversation
useScan registered 11 listeners with sequential await listen() and no
cancellation guard, so a cleanup during registration (StrictMode remount)
orphaned every listener that resolved afterwards — duplicating scan event
processing and inflating telemetry. Register via Promise.all with a
cancelled flag, mirroring the menu-listener fix in App.tsx.
useChromecast had the same race in miniature: an unmount while
listen("cast://status") was in flight leaked the listener.
moveFocusBy ran selection emits, cast-redirect scheduling, and even onOpenChannel (which starts playback) inside its setFocusedRow updater, and updateSelection called emitSelection inside setSelectedIndices. Updater functions must be pure: StrictMode double-invokes them in dev — arrow-key navigation could start playback twice — and concurrent React may re-run them in production. Compute the next value first, then apply state and side effects outside the updater.
fetch_xtream_account_info used .ok()? inside its two-endpoint fallback loop, so a transport error, body-read failure, or invalid JSON from the get_account_info endpoint returned None for the whole function without ever trying plain player_api.php. Convert those early returns into logged continues so the fallback endpoint actually runs.
overscan is a row count, not pixels: 250 mounted ~500 extra ChannelRows beyond the ~25 visible ones, and every scan batch flush reconciled all of them — largely defeating the virtualization. The mac header-reveal band only needs a handful of rows above the viewport (it filters to within toolbarHeight of the top edge), so 20 keeps it populated.
If cancellation landed right after a channel check completed, compute_shared_url_result rewrote the finished verdict — possibly Alive — to Dead, then emitted and checkpointed that false result. Return AppError::Cancelled instead (the worker already exits cleanly on it), so the channel stays unscanned and resume re-checks it, consistent with the diagnostics cancellation paths.
The checker returned statuses as plain strings ("Alive", "DRM",
"Geoblocked (Confirmed)", …) that scan.rs re-parsed in two
hand-maintained match blocks. The quick-check block was missing
"Placeholder", silently degrading placeholder results to Dead — and
any future typo or new variant would fail the same way. CheckOutcome,
the internal attempt structs, and proxy::confirm_geoblock now carry
ChannelStatus directly; debug-log verdict strings come from its
Display impl, which already produced the identical labels.
The stream proxy validated only the initial upstream URL while its reqwest clients silently followed up to 10 redirects, so a public upstream that 302'd to 127.0.0.1, 169.254.169.254, or another private target sailed straight past the private-network block. The proxy clients now use redirect::Policy::none() and all three upstream fetch paths (scheme handler, raw TCP stream, reconnect loop) hop redirects manually via fetch_with_hop_validation, re-running is_safe_upstream_url on every hop. The cast proxy is deliberately unchanged: it has no private-network guard because casting LAN sources (e.g. a local Dispatcharr server) to a LAN receiver is a legitimate, common flow.
handle_proxy_request buffered entire upstream responses with bytes() and checker::verify() read manifest bodies with text(), neither with a size limit — the 30s timeouts bound time, not bytes, so a misclassified live MPEG-TS behind an .m3u8-looking URL could push hundreds of MB into memory per request. Move the cast proxy's capped streaming reader into a shared engine::proxy_common module and use it in both places: 64 MiB for buffered scheme-handler responses (manifests and VOD segments), 8 MiB for liveness-check manifests (over the cap now verdicts Dead with an explicit reason instead of buffering).
Cancellation via token is handled, but if the enclosing future is dropped instead — task aborted, runtime shutdown mid-scan, app exit — the spawned ffmpeg/ffprobe would be orphaned and a process stuck on a stalling stream could live indefinitely. The proxies' spawns already set kill_on_drop; these three (tool runner, bitrate profiler, combined diagnostics) now match.
spawn_upstream_pump retried a dead upstream every 200ms indefinitely — 5 requests per second for the whole cast session against a server returning 404s, which is the exact pattern IPTV providers ban for. Consecutive failures (error status, connect failure, or a connection that broke without delivering a byte) now grow the delay exponentially up to 5s, and a connection that actually produced data resets it, so genuine mid-stream reconnects stay fast.
The double-checked lazy-start dance existed in three copies: the canonical stream_proxy::ensure_streaming_proxy_port (the only one that TCP-probes the stored port and restarts a dead listener) had no callers, while ffmpeg.rs and player.rs each kept a private probe-less copy that would happily return a stale port after the listener died. Both now delegate to the canonical version, so every path gets the liveness check.
recent.rs carried its own normalize_xtream_server that didn't strip a trailing /get.php (and silently dropped query strings instead of rejecting them), so recent-entry dedup keys could disagree with the saved-playlist and source-identity keys built from playlist.rs — producing duplicate recents or failed backfills for .../get.php URLs. It now delegates to playlist::normalize_xtream_server. The copy-pasted xtream_host_label in saved.rs and recent.rs is also collapsed into one shared helper.
playlist.rs, scan.rs, and history.rs (twice) each hand-rolled the same strip-fragment/strip-default-port logic feeding dedup keys, history identity keys, and shared-URL scan caching. A future edit to one copy would silently desynchronize history diffs from scan dedup. One module now owns the three shapes (parsed-URL identity, raw stream URL with fallback, http/https-only identity).
The write-tmp/rename/fallback-remove-and-rename/cleanup dance was implemented three times (remote playlist cache persist, cached-bytes writer, history v2 index). One helper in engine::disk now owns it, including the Windows rename-over-existing fallback. The playlist persist path loses only the sub-step "Finalizing cache entry" progress detail, which flashed for the duration of a rename.
Parsing a playlist (up to the 200 MB download cap) is seconds of CPU-bound work, and it ran inline in async commands — pinning a tokio worker that is simultaneously driving proxies and scan events. The three copy-pasted parse-progress blocks in playlist.rs collapse into one parse_playlist_off_thread helper that runs the parser under spawn_blocking; scan.rs now also parses off-thread and does the same for checkpoint loading, scan-history writes (which serialize the full results Vec and re-read the previous run for diffing), and the cache file write in the download path.
The cache held up to 8 full PlaylistPreviews by value and deep-copied one on every hit and insert — for a 100k-channel playlist that's hundreds of MB of redundant allocation across opens and scan starts. Entries are now Arc<PlaylistPreview>: hits hand out a reference, and the scan start clones only the channel list it actually mutates.
ChannelDebugLog embedded complete ffprobe/ffmpeg output and is held in memory for the entire scan, cloned into scan history, and stored in the window scan log — an unbounded multi-MB tool dump multiplied across tens of thousands of channels. Retained diagnostics are now capped at 64 KiB per channel, keeping the tail (errors conclude a tool run) with an explicit truncation marker.
Each accepted channel re-ran the full attribute char-walk up to seven times: group extraction (twice), language detection, four tvg-* lookups via extract_tvg_metadata, plus again in is_line_needed under a group filter. The hot loop now parses the attribute list once per EXTINF line and threads it (with the derived group name) through filtering and channel construction; the public per-line helpers remain as thin wrappers for external callers.
profile_bitrate inlined a verbatim copy of parse_bytes_read's Statistics-line parsing, and the 1 MB tail-buffer stderr drain task was copy-pasted between profile_bitrate and run_combined_diagnostics. Both now share spawn_stderr_tail_reader and parse_bytes_read.
RemuxState.child was always stored as None (the remux worker task owns and kills the ffmpeg child), so the start_kill in cleanup_blocking and the graceful_kill in cleanup_remux_async were dead code that misleadingly suggested cleanup could terminate ffmpeg. Cleanup now just removes the temp directory, and a comment points at the real owner.
delay_for_payload allocated a fresh tail+payload Vec for every 64 KB chunk on the playback hot path just to look for PCRs. The payload is now scanned in place; only the chunk boundary (previous <=752-byte tail plus the first packets of the new payload) is stitched, to catch a PCR packet straddling two chunks.
Both raw-TCP proxies parsed the HTTP request from a single 8 KB read. If the request line and headers arrived split across TCP segments (seen with some Cast receivers sending Range headers), parsing silently failed with a 400 or dropped the Range header. A shared helper now reads until \r\n\r\n, still bounded at 8 KB.
The dependency floated on the default branch of an external repo; a cargo update could silently pull breaking or unreviewed changes into debug builds. Pin the rev Cargo.lock already resolved.
check_channel_status_with_ffprobe_debug and _with_debug each declared an identical AttemptOutcome struct and duplicated the base-pass / if-Dead-run-extended-pass / merge logic. One module-level struct and a generic run_attempts_with_extended_timeout driver now own that flow; only the probe bodies differ. Also scopes the AsyncReadExt import to the proxy test modules where it's actually used.
The two ~150-line menu closures were identical except for the modifier key (Cmd vs Ctrl), the macOS-only app and Window menus, and whether Settings/Quit live in the app menu or the File menu. One builder now covers all platforms with an accelerator helper and two small cfg blocks, so future menu items can't be added to one platform and forgotten on the other.
The menu event handler listed ten hand-written arms each for menu.file.recent.N and menu.file.saved.N. A small suffix parser now handles any index, so raising the recent/saved menu capacity can't silently break event forwarding.
Setup waited synchronously (with no timeout) for the proxy listener to bind before letting window creation proceed — a pathological bind hang meant the app never launched. The wait existed to prevent an early Play click racing the port, but every play path now goes through ensure_streaming_proxy_port, which shares the start lock and probes liveness, so the warm-up can be a plain background task.
The stream proxy and cast proxy each carried a near-identical manifest line-walker and URI="..." tag parser — two divergent copies of the trickiest parsing in the proxy layer. proxy_common now owns a single rewriter parameterized by a URL mapper closure (returning None leaves a reference unrewritten, which expresses the cast proxy's origin filter), plus the shared is_m3u8 detection.
App.tsx mixed playlist source management, update checking, native menu plumbing, the start screen, and six ad-hoc banners into one 3,200-line component. Extracted along its natural seams: - usePlaylistSources: recent/saved refresh, loadAndCommitSource, the open handlers (path/URL/Xtream/Stalker/saved/recent), saved-playlist CRUD, and source-filter application - useUpdateCheck: version compare, cooldown cache, checkForUpdates - useMenuEventBridge: all menu:// listeners; one render-updated handlersRef replaces ~20 individual ref+effect mirror pairs (the Promise.all + cancelled-flag registration pattern is preserved) - StartScreen: presentational empty-state / load-progress / lists - AppBanners: the six banners behind a shared useAutoDismiss helper - lib/errors and lib/recentPlaylists: formatting helpers the extracted modules share App.tsx drops from 3,199 to 1,682 lines with no behavior change.
The hook returned a fresh object literal each render, so App's chromecast useMemo keyed on it never hit and every App render pushed a new chromecast prop into SelectedChannelSidebar, ThumbnailPanel, and CastMenu — ThumbnailPanel explicitly documents depending on prop stability. Memoize over the actual state fields.
✅ Action performedReview finished.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bb5d1bca7f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src-tauri/src/commands/server_test.rs`:
- Around line 77-83: Extract the duplicated reqwest::Client construction used by
fetch_xtream_stream_ids, discover_working_channels, probe_server_channels, and
test_single_server_api into one shared helper accepting the request timeout.
Preserve the common limited-redirect, connect-timeout, invalid-certificate, and
user-agent settings, while retaining each caller’s existing timeout and error
mapping.
- Around line 100-104: Update the live-stream fetch error handling around
build_xtream_player_api_action_url so reqwest::Error messages cannot expose
Xtream username/password query parameters; sanitize or replace the URL before
storing the message in last_error. Apply the same redaction to the parse-failure
path that formats reqwest::Error, while preserving the existing error context
and retry flow.
In `@src-tauri/src/commands/settings.rs`:
- Line 168: Update the preset name maximum-length rejection in the settings
command to return AppError::Validation instead of AppError::Other, matching the
existing empty-name validation branch. Keep the length check and error behavior
otherwise unchanged.
In `@src-tauri/src/engine/remote_cache.rs`:
- Around line 151-156: Gate invalid TLS certificate acceptance behind the
explicit local/trustless IPTV source setting in the reqwest clients used by the
remote-cache downloader and both Xtream request paths. Update the client
builders in remote_cache.rs (151-156) and xtream.rs (381-387, 527-533) so normal
certificate verification remains enabled by default and
.danger_accept_invalid_certs(true) is applied only when that setting is enabled.
In `@src-tauri/src/engine/xtream.rs`:
- Around line 362-366: Update the M3U generation in the relevant Xtream live,
VOD, and series blocks to escape provider-supplied EXTINF values before writing
them. Reuse or extract the existing escape_extinf_attribute helper from
engine::stalker::build_stalker_preview, applying it to tvg_id, tvg_logo, group,
and name while preserving the current stream URL output.
In `@src/App.tsx`:
- Around line 1253-1261: Move the shortcutHandlersRef.current assignment out of
the render body and into an effect that runs after committed renders, using the
existing shortcutHandlers value as the effect dependency. Keep the keydown
handler reading shortcutHandlersRef.current at event time and preserve the
current handler object contents.
In `@src/hooks/usePlaylistSources.ts`:
- Around line 326-337: Replace the raw sourceLabel with the redacted safeLabel
in both debug logging branches within the cached preview handling, including the
channel-search and source-loaded messages, while preserving the existing message
structure and values.
In `@src/hooks/useUpdateCheck.ts`:
- Around line 96-144: Update the latest-release fetch in checkForUpdates to use
a bounded AbortSignal timeout and ensure cancellation or an obsolete request
cannot apply subsequent store or localStorage updates. Preserve the existing
success, force-message, and error handling behavior while treating aborts safely
through the existing catch flow.
In `@src/lib/errors.ts`:
- Around line 21-42: Extract the shared error normalization and
conditional-prefix behavior from formatPlaylistOpenError and
formatSourceReloadError into a helper parameterized by the message label and any
prefix-removal pattern. Update both functions to use the helper while preserving
their existing fallback messages, prefix text, and playlist-prefix normalization
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 169ef18c-f399-4624-bbdc-f05755a02ff5
⛔ Files ignored due to path filters (1)
src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (60)
src-tauri/Cargo.tomlsrc-tauri/examples/backend_bench.rssrc-tauri/src/commands/history.rssrc-tauri/src/commands/mod.rssrc-tauri/src/commands/player.rssrc-tauri/src/commands/playlist.rssrc-tauri/src/commands/recent.rssrc-tauri/src/commands/saved.rssrc-tauri/src/commands/scan.rssrc-tauri/src/commands/server_test.rssrc-tauri/src/commands/settings.rssrc-tauri/src/engine/cast_proxy.rssrc-tauri/src/engine/checker.rssrc-tauri/src/engine/disk.rssrc-tauri/src/engine/ffmpeg.rssrc-tauri/src/engine/mod.rssrc-tauri/src/engine/parser.rssrc-tauri/src/engine/proxy.rssrc-tauri/src/engine/proxy_common.rssrc-tauri/src/engine/remote_cache.rssrc-tauri/src/engine/stalker.rssrc-tauri/src/engine/stream_proxy.rssrc-tauri/src/engine/xtream.rssrc-tauri/src/error.rssrc-tauri/src/lib.rssrc-tauri/src/models/channel.rssrc-tauri/src/models/saved_playlist.rssrc-tauri/src/models/scan.rssrc-tauri/src/models/scan_log.rssrc-tauri/src/state.rssrc-tauri/src/urlnorm.rssrc-tauri/tests/scan_pipeline_integration.rssrc/App.tsxsrc/components/AppBanners.tsxsrc/components/ChannelTable.tsxsrc/components/StartScreen.tsxsrc/components/Toolbar.tsxsrc/hooks/useChromecast.tssrc/hooks/useMenuEventBridge.tssrc/hooks/usePlaylistSources.tssrc/hooks/useScan.helpers.tssrc/hooks/useScan.tssrc/hooks/useStreamPlayer.tssrc/hooks/useUpdateCheck.tssrc/lib/channelResults.tssrc/lib/errors.tssrc/lib/filters.tssrc/lib/playback.tssrc/lib/recentPlaylists.tssrc/lib/runtimeMonitor.tssrc/lib/types.tssrc/store/slices/scanSlice.tssrc/store/slices/uiSlice.tssrc/store/types.tstests/channelResults.test.tstests/duplicates.test.tstests/exportScope.test.tstests/filters.sort.test.tstests/format.test.tstests/useStreamPlayer.test.ts
💤 Files with no reviewable changes (7)
- src/lib/types.ts
- tests/filters.sort.test.ts
- tests/exportScope.test.ts
- tests/format.test.ts
- tests/duplicates.test.ts
- src/hooks/useScan.helpers.ts
- src/store/slices/scanSlice.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
src-tauri/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Use Rust conventions: snake_case naming, 4-space indentation,
thiserrorfor error types, andserdefor serialization.
Files:
src-tauri/src/commands/mod.rssrc-tauri/src/urlnorm.rssrc-tauri/src/models/saved_playlist.rssrc-tauri/tests/scan_pipeline_integration.rssrc-tauri/src/engine/proxy.rssrc-tauri/src/error.rssrc-tauri/src/engine/disk.rssrc-tauri/src/models/scan_log.rssrc-tauri/src/engine/mod.rssrc-tauri/src/models/channel.rssrc-tauri/src/commands/player.rssrc-tauri/src/engine/proxy_common.rssrc-tauri/examples/backend_bench.rssrc-tauri/src/commands/history.rssrc-tauri/src/state.rssrc-tauri/src/commands/saved.rssrc-tauri/src/commands/server_test.rssrc-tauri/src/commands/settings.rssrc-tauri/src/engine/stalker.rssrc-tauri/src/engine/parser.rssrc-tauri/src/engine/remote_cache.rssrc-tauri/src/commands/recent.rssrc-tauri/src/engine/xtream.rssrc-tauri/src/lib.rssrc-tauri/src/models/scan.rssrc-tauri/src/engine/stream_proxy.rssrc-tauri/src/engine/ffmpeg.rssrc-tauri/src/commands/playlist.rssrc-tauri/src/engine/cast_proxy.rssrc-tauri/src/commands/scan.rssrc-tauri/src/engine/checker.rs
src-tauri/src/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
src-tauri/src/**/*.rs: Keep backend responsibilities organized intomodels,engine,commands,state.rs,error.rs, andlib.rsaccording to their documented roles.
Use event-driven scanning: emitscan://channel-resultevents per channel, with frontend event updates batched usingrequestAnimationFrame.
Files:
src-tauri/src/commands/mod.rssrc-tauri/src/urlnorm.rssrc-tauri/src/models/saved_playlist.rssrc-tauri/src/engine/proxy.rssrc-tauri/src/error.rssrc-tauri/src/engine/disk.rssrc-tauri/src/models/scan_log.rssrc-tauri/src/engine/mod.rssrc-tauri/src/models/channel.rssrc-tauri/src/commands/player.rssrc-tauri/src/engine/proxy_common.rssrc-tauri/src/commands/history.rssrc-tauri/src/state.rssrc-tauri/src/commands/saved.rssrc-tauri/src/commands/server_test.rssrc-tauri/src/commands/settings.rssrc-tauri/src/engine/stalker.rssrc-tauri/src/engine/parser.rssrc-tauri/src/engine/remote_cache.rssrc-tauri/src/commands/recent.rssrc-tauri/src/engine/xtream.rssrc-tauri/src/lib.rssrc-tauri/src/models/scan.rssrc-tauri/src/engine/stream_proxy.rssrc-tauri/src/engine/ffmpeg.rssrc-tauri/src/commands/playlist.rssrc-tauri/src/engine/cast_proxy.rssrc-tauri/src/commands/scan.rssrc-tauri/src/engine/checker.rs
src-tauri/src/commands/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Implement Tauri IPC commands in the commands module, organized by playlist, scan, export, and settings responsibilities.
Files:
src-tauri/src/commands/mod.rssrc-tauri/src/commands/player.rssrc-tauri/src/commands/history.rssrc-tauri/src/commands/saved.rssrc-tauri/src/commands/server_test.rssrc-tauri/src/commands/settings.rssrc-tauri/src/commands/recent.rssrc-tauri/src/commands/playlist.rssrc-tauri/src/commands/scan.rs
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Define agent interfaces and implementations in TypeScript for agent systems
Use clear naming conventions for agent classes and methods
Document agent behavior and responsibilities in comments or docstrings
Implement error handling in agent methods
Files:
src/lib/errors.tssrc/hooks/useMenuEventBridge.tssrc/lib/recentPlaylists.tstests/useStreamPlayer.test.tssrc/hooks/useUpdateCheck.tssrc/lib/filters.tssrc/hooks/useChromecast.tssrc/lib/channelResults.tssrc/lib/runtimeMonitor.tssrc/components/StartScreen.tsxsrc/components/Toolbar.tsxsrc/components/ChannelTable.tsxsrc/hooks/usePlaylistSources.tstests/channelResults.test.tssrc/store/slices/uiSlice.tssrc/hooks/useScan.tssrc/store/types.tssrc/lib/playback.tssrc/components/AppBanners.tsxsrc/hooks/useStreamPlayer.tssrc/App.tsx
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use strict TypeScript with no unused locals or parameters.
Files:
src/lib/errors.tssrc/hooks/useMenuEventBridge.tssrc/lib/recentPlaylists.tssrc/hooks/useUpdateCheck.tssrc/lib/filters.tssrc/hooks/useChromecast.tssrc/lib/channelResults.tssrc/lib/runtimeMonitor.tssrc/components/StartScreen.tsxsrc/components/Toolbar.tsxsrc/components/ChannelTable.tsxsrc/hooks/usePlaylistSources.tssrc/store/slices/uiSlice.tssrc/hooks/useScan.tssrc/store/types.tssrc/lib/playback.tssrc/components/AppBanners.tsxsrc/hooks/useStreamPlayer.tssrc/App.tsx
src-tauri/src/engine/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Place core IPTV processing logic, including parsing, checking, ffmpeg, proxy, and resume functionality, in the engine module.
Files:
src-tauri/src/engine/proxy.rssrc-tauri/src/engine/disk.rssrc-tauri/src/engine/mod.rssrc-tauri/src/engine/proxy_common.rssrc-tauri/src/engine/stalker.rssrc-tauri/src/engine/parser.rssrc-tauri/src/engine/remote_cache.rssrc-tauri/src/engine/xtream.rssrc-tauri/src/engine/stream_proxy.rssrc-tauri/src/engine/ffmpeg.rssrc-tauri/src/engine/cast_proxy.rssrc-tauri/src/engine/checker.rs
src/components/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Implement components as functional React components using hooks, and use Tailwind CSS for styling instead of CSS-in-JS.
Files:
src/components/StartScreen.tsxsrc/components/Toolbar.tsxsrc/components/ChannelTable.tsxsrc/components/AppBanners.tsx
src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use a virtualized table via
@tanstack/react-virtualfor playlists containing 1000 or more channels.
Files:
src/components/StartScreen.tsxsrc/components/Toolbar.tsxsrc/components/ChannelTable.tsxsrc/components/AppBanners.tsx
🪛 ast-grep (0.44.1)
src-tauri/src/commands/server_test.rs
[warning] 76-80: Dangerously accepting invalid TLS
Context: reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.connect_timeout(PLAYLIST_DOWNLOAD_CONNECT_TIMEOUT)
.timeout(XTREAM_JSON_API_TIMEOUT)
.danger_accept_invalid_certs(true)
Note: [CWE-295]: Improper Certificate
(reqwest-accept-invalid-rust)
[warning] 179-183: Dangerously accepting invalid TLS
Context: reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.connect_timeout(PLAYLIST_DOWNLOAD_CONNECT_TIMEOUT)
.timeout(SERVER_TEST_DISCOVERY_HTTP_TIMEOUT)
.danger_accept_invalid_certs(true)
Note: [CWE-295]: Improper Certificate
(reqwest-accept-invalid-rust)
[warning] 262-266: Dangerously accepting invalid TLS
Context: reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.connect_timeout(PLAYLIST_DOWNLOAD_CONNECT_TIMEOUT)
.timeout(SERVER_TEST_STREAM_TIMEOUT)
.danger_accept_invalid_certs(true)
Note: [CWE-295]: Improper Certificate
(reqwest-accept-invalid-rust)
[warning] 371-375: Dangerously accepting invalid TLS
Context: reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.connect_timeout(PLAYLIST_DOWNLOAD_CONNECT_TIMEOUT)
.timeout(XTREAM_PLAYER_API_TIMEOUT)
.danger_accept_invalid_certs(true)
Note: [CWE-295]: Improper Certificate
(reqwest-accept-invalid-rust)
src-tauri/src/engine/remote_cache.rs
[warning] 150-154: Dangerously accepting invalid TLS
Context: reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.connect_timeout(connect_timeout)
.timeout(timeout)
.danger_accept_invalid_certs(true)
Note: [CWE-295]: Improper Certificate
(reqwest-accept-invalid-rust)
src-tauri/src/engine/xtream.rs
[warning] 380-384: Dangerously accepting invalid TLS
Context: reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.connect_timeout(PLAYLIST_DOWNLOAD_CONNECT_TIMEOUT)
.timeout(XTREAM_JSON_API_TIMEOUT)
.danger_accept_invalid_certs(true)
Note: [CWE-295]: Improper Certificate
(reqwest-accept-invalid-rust)
[warning] 526-530: Dangerously accepting invalid TLS
Context: reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.connect_timeout(PLAYLIST_DOWNLOAD_CONNECT_TIMEOUT)
.timeout(XTREAM_PLAYER_API_TIMEOUT)
.danger_accept_invalid_certs(true)
Note: [CWE-295]: Improper Certificate
(reqwest-accept-invalid-rust)
🪛 React Doctor (0.7.6)
src/App.tsx
[error] 1261-1261: This ref is mutated during render. React can replay or discard render work, so the mutation can leak from UI that never commits.
Move ref writes into an event handler or effect. Render must stay pure because React can replay or discard it. The predictable null-guarded lazy initialization pattern remains supported.
(no-ref-current-in-render)
[warning] 1261-1261: This component misses React Compiler's automatic memoization & re-renders more than it should: Cannot access refs during render. Rewrite the flagged code so the compiler can optimize it.
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the current property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
(refs)
🔇 Additional comments (60)
src/lib/filters.ts (1)
331-390: LGTM!src-tauri/src/commands/settings.rs (1)
679-724: LGTM!src/components/AppBanners.tsx (1)
1-171: LGTM!src/components/ChannelTable.tsx (1)
5-6: LGTM!Also applies to: 317-324, 358-362, 383-397, 682-720
src/components/Toolbar.tsx (1)
47-48: LGTM!Also applies to: 173-180, 200-206
src/hooks/useChromecast.ts (1)
1-1: LGTM!Also applies to: 92-92, 110-116, 129-134
src/hooks/useMenuEventBridge.ts (1)
1-136: LGTM!src/hooks/useScan.ts (1)
287-298: LGTM!Also applies to: 294-433, 434-482
src/hooks/useUpdateCheck.ts (1)
1-186: LGTM!src/lib/recentPlaylists.ts (2)
36-59: LGTM!
6-15: 🔒 Security & PrivacyManual verification needed for recent Xtream credential storage.
The available evidence is inconclusive: direct repository access failed, and public codebase indexes do not align precisely with the referenced
src/lib/recentPlaylists.tsserializer.src/lib/errors.ts (1)
21-42: 🔒 Security & PrivacyVerify backend error text redaction for Xtream credentials.
The repository is unavailable for inspection, so it cannot be determined from the current evidence whether
formatPlaylistOpenError()orformatSourceReloadError()can surface raw backend error strings containing Xtream source URLs withusernameand/orpasswordquery parameters. Check the upstream Rust error path/formatters and add query-credential redaction before rendering.src-tauri/Cargo.toml (1)
39-41: LGTM!src-tauri/src/engine/parser.rs (1)
160-219: LGTM!Also applies to: 317-335, 349-370, 417-418, 439-446, 465-484
src-tauri/src/commands/history.rs (1)
92-109: LGTM!Also applies to: 165-170, 275-286
src/App.tsx (1)
195-239: LGTM!Also applies to: 662-696
src/hooks/usePlaylistSources.ts (1)
235-266: LGTM!src/components/StartScreen.tsx (1)
33-248: LGTM!src-tauri/src/commands/player.rs (1)
181-183: LGTM!src-tauri/src/commands/playlist.rs (2)
526-570: LGTM!
707-729: LGTM!src-tauri/src/commands/recent.rs (1)
35-41: LGTM!Also applies to: 99-99
src-tauri/src/commands/saved.rs (1)
80-80: LGTM!src-tauri/src/engine/mod.rs (1)
9-15: LGTM!src-tauri/src/engine/stalker.rs (1)
409-521: LGTM!src-tauri/src/urlnorm.rs (1)
12-45: LGTM!src-tauri/src/lib.rs (3)
578-654: LGTM!
771-794: LGTM!
880-894: LGTM!src-tauri/examples/backend_bench.rs (1)
179-190: LGTM!src-tauri/src/commands/scan.rs (3)
139-193: LGTM!Also applies to: 277-312
1354-1481: LGTM!Also applies to: 1564-1798, 1800-2091
2135-2303: LGTM!Also applies to: 2391-2426, 2454-2595
src-tauri/src/engine/checker.rs (3)
699-728: LGTM!Also applies to: 841-892
894-1140: LGTM!Also applies to: 1145-1417, 1874-2091
1420-1458: 🗄️ Data Integrity & IntegrationConfirm in-crate checks_channel_status consumers are updated
Manual confirmation is still needed for the repository because the codebase scan failed. Check any remaining
check_channel_status/ChannelCheckOutcome.statusconsumers for string-literal comparisons or format strings that still expectAlive/Dead/Drm/DRM/Geoblocked/Placeholderas aString.src-tauri/src/engine/disk.rs (1)
11-39: LGTM!src-tauri/src/models/scan_log.rs (1)
38-73: LGTM!src-tauri/src/state.rs (1)
25-41: LGTM!Also applies to: 139-174
src-tauri/src/error.rs (1)
23-31: LGTM!src-tauri/src/models/scan.rs (1)
74-124: LGTM!src-tauri/tests/scan_pipeline_integration.rs (1)
181-181: LGTM!Also applies to: 216-216
src-tauri/src/models/channel.rs (1)
83-83: LGTM!src-tauri/src/commands/mod.rs (1)
9-9: LGTM!src-tauri/src/commands/server_test.rs (1)
77-83: 🔒 Security & Privacy | 💤 Low valueConfirm
danger_accept_invalid_certs(true)is an intentional, accepted trade-off.Static analysis flags this on all four client builders (Lines 77, 180, 263, 372). Disabling certificate validation while sending credential-bearing Xtream URLs makes the connection MITM-able, which could expose those credentials. If this mirrors an established codebase-wide policy for IPTV endpoints, it's acceptable; please confirm rather than leave it implicit.
Source: Linters/SAST tools
src-tauri/src/models/saved_playlist.rs (1)
11-17: LGTM!src/lib/channelResults.ts (1)
44-48: LGTM!Also applies to: 56-60
tests/channelResults.test.ts (1)
95-98: LGTM!Also applies to: 108-116
src/store/slices/uiSlice.ts (1)
44-83: LGTM!src/store/types.ts (1)
168-173: 🎯 Functional CorrectnessNo remaining
toggleSidebarupdates needed.
toggleSidebarhas no remaining TypeScript/TSX references to migrate.src-tauri/src/engine/cast_proxy.rs (1)
36-43: LGTM!Also applies to: 95-108, 629-631, 652-730, 758-770, 882-889, 1482-1484, 1511-1529, 1543-1543
src-tauri/src/engine/proxy_common.rs (1)
1-24: LGTM!Also applies to: 26-122, 124-163, 165-193
src-tauri/src/engine/stream_proxy.rs (1)
3-3: LGTM!Also applies to: 27-32, 134-146, 280-328, 344-435, 490-507, 573-601, 1049-1082, 1179-1356, 1390-1393
src-tauri/src/engine/ffmpeg.rs (1)
2-2: LGTM!Also applies to: 407-407, 697-801, 1473-1608, 1842-1850
src-tauri/src/engine/proxy.rs (1)
196-217: LGTM!src/hooks/useStreamPlayer.ts (1)
2-29: LGTM!Also applies to: 430-448
src/lib/playback.ts (2)
1-131: LGTM!Also applies to: 144-369
133-142: 🎯 Functional CorrectnessManual verification needed: ensure the backend accepts
remux=1.The available repository access failed, so the parser can’t be checked against
toStreamingProxyUrl’s emitted&remux=1vs the backend’s accepted value.src/lib/runtimeMonitor.ts (1)
1-102: LGTM!Also applies to: 104-232, 234-370, 372-444
tests/useStreamPlayer.test.ts (1)
22-22: LGTM!
|
@coderabbitai review |
|
@codex review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b4669294fc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src-tauri/src/commands/server_test.rs (1)
495-510: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winBound the concurrent server-test work.
test_xtream_servers_innerschedules each API test in Phase 1 withjoin_all, and Phase 3 schedules each server’s probe work into anotherjoin_all; each probe path can emit HTTP requests,ffprobe, and screenshot jobs. Use a shared concurrency limit such asbuffer_unorderedor atokio::sync::Semaphorefor the Phase 1 and Phase 3 futures so large pasted lists do not open unbounded work.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src-tauri/src/commands/server_test.rs` around lines 495 - 510, Bound concurrency in test_xtream_servers_inner for both Phase 1 API tests and Phase 3 probe futures instead of awaiting unbounded join_all collections. Use a shared tokio::sync::Semaphore or bounded buffer_unordered limit across both phases, preserving each result’s association with its raw server and existing probe behavior.src/hooks/usePlaylistSources.ts (1)
322-366: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy liftPrevent an older source load from overwriting a newer one.
Two opens can overlap at
await loadFullSourcePreview(). Whichever resolves last commits its preview, descriptor, and scan initialization—even if the user selected a newer source. Add a request generation or cancellation guard so only the latest load can commit state and clear loading progress.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/usePlaylistSources.ts` around lines 322 - 366, Protect the async load flow around loadFullSourcePreview and commitLoadedPlaylist with a request-generation or cancellation guard. Ensure each new source load invalidates prior requests, and verify the request is still current before committing the preview, descriptor, scan initialization, or clearing loading progress; stale loads must exit without updating state.src/App.tsx (1)
1252-1264: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDon’t depend on the freshly created shortcut-handler object.
shortcutHandlersis a new object on every render, so this layout effect re-runs each time and re-assigns the ref unnecessarily. Depend on the individual callbacks, or keep the ref assignment inside an effect with no object dependency.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/App.tsx` around lines 1252 - 1264, Update the shortcutHandlersRef synchronization in the App component so it does not depend on the freshly created shortcutHandlers object. Use the individual callback dependencies in the useLayoutEffect dependency array, while preserving the existing ref assignment and handler behavior.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src-tauri/src/commands/playlist.rs`:
- Around line 420-430: Update AppSettings::default() so accept_invalid_certs is
false for fresh installations and missing settings.json files. Preserve
accepts_invalid_certs as the runtime reader of the configured value, without
adding an implicit enablement in the scan profile flow.
In `@src-tauri/src/commands/server_test.rs`:
- Around line 36-47: The build_server_test_client flow must stop using automatic
redirects, which cannot validate intermediate destinations. Configure the client
with reqwest::redirect::Policy::none(), then update the server-testing request
flow to follow redirects manually and validate every hop using the existing
stream-proxy approach from fetch_with_hop_validation, rejecting loopback,
private, and metadata-network destinations before each request.
In `@src-tauri/src/engine/xtream.rs`:
- Around line 271-279: Extract the duplicated ReadCappedError-to-message match
into a shared helper near the existing Xtream fetch functions, preserving the
TooLarge wording and Read(error).without_url() formatting. Update both
fetch_xtream_json_array and fetch_xtream_account_info to call the helper before
logging, removing their inline matches.
In `@src/lib/errors.ts`:
- Line 58: Update the prefix check and return expression in the
error-normalization logic to use normalized rather than raw. Test whether
normalized already starts with the existing prefix, return normalized when it
does, and otherwise prepend the prefix once to prevent duplicate messages.
---
Outside diff comments:
In `@src-tauri/src/commands/server_test.rs`:
- Around line 495-510: Bound concurrency in test_xtream_servers_inner for both
Phase 1 API tests and Phase 3 probe futures instead of awaiting unbounded
join_all collections. Use a shared tokio::sync::Semaphore or bounded
buffer_unordered limit across both phases, preserving each result’s association
with its raw server and existing probe behavior.
In `@src/App.tsx`:
- Around line 1252-1264: Update the shortcutHandlersRef synchronization in the
App component so it does not depend on the freshly created shortcutHandlers
object. Use the individual callback dependencies in the useLayoutEffect
dependency array, while preserving the existing ref assignment and handler
behavior.
In `@src/hooks/usePlaylistSources.ts`:
- Around line 322-366: Protect the async load flow around loadFullSourcePreview
and commitLoadedPlaylist with a request-generation or cancellation guard. Ensure
each new source load invalidates prior requests, and verify the request is still
current before committing the preview, descriptor, scan initialization, or
clearing loading progress; stale loads must exit without updating state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 98fea0bc-e8f9-415a-a1b4-51e8fdad7f36
📒 Files selected for processing (12)
src-tauri/src/commands/playlist.rssrc-tauri/src/commands/server_test.rssrc-tauri/src/commands/settings.rssrc-tauri/src/engine/parser.rssrc-tauri/src/engine/remote_cache.rssrc-tauri/src/engine/stalker.rssrc-tauri/src/engine/xtream.rssrc/App.tsxsrc/hooks/usePlaylistSources.tssrc/hooks/useUpdateCheck.tssrc/lib/errors.tstests/errors.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Define agent interfaces and implementations in TypeScript for agent systems
Use clear naming conventions for agent classes and methods
Document agent behavior and responsibilities in comments or docstrings
Implement error handling in agent methods
Files:
tests/errors.test.tssrc/lib/errors.tssrc/hooks/usePlaylistSources.tssrc/hooks/useUpdateCheck.tssrc/App.tsx
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use strict TypeScript with no unused locals or parameters.
Files:
src/lib/errors.tssrc/hooks/usePlaylistSources.tssrc/hooks/useUpdateCheck.tssrc/App.tsx
src-tauri/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Use Rust conventions: snake_case naming, 4-space indentation,
thiserrorfor error types, andserdefor serialization.
Files:
src-tauri/src/commands/settings.rssrc-tauri/src/engine/parser.rssrc-tauri/src/commands/server_test.rssrc-tauri/src/engine/remote_cache.rssrc-tauri/src/commands/playlist.rssrc-tauri/src/engine/xtream.rssrc-tauri/src/engine/stalker.rs
src-tauri/src/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
src-tauri/src/**/*.rs: Keep backend responsibilities organized intomodels,engine,commands,state.rs,error.rs, andlib.rsaccording to their documented roles.
Use event-driven scanning: emitscan://channel-resultevents per channel, with frontend event updates batched usingrequestAnimationFrame.
Files:
src-tauri/src/commands/settings.rssrc-tauri/src/engine/parser.rssrc-tauri/src/commands/server_test.rssrc-tauri/src/engine/remote_cache.rssrc-tauri/src/commands/playlist.rssrc-tauri/src/engine/xtream.rssrc-tauri/src/engine/stalker.rs
src-tauri/src/commands/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Implement Tauri IPC commands in the commands module, organized by playlist, scan, export, and settings responsibilities.
Files:
src-tauri/src/commands/settings.rssrc-tauri/src/commands/server_test.rssrc-tauri/src/commands/playlist.rs
src-tauri/src/engine/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Place core IPTV processing logic, including parsing, checking, ffmpeg, proxy, and resume functionality, in the engine module.
Files:
src-tauri/src/engine/parser.rssrc-tauri/src/engine/remote_cache.rssrc-tauri/src/engine/xtream.rssrc-tauri/src/engine/stalker.rs
🪛 React Doctor (0.7.6)
src/App.tsx
[error] 1264-1264: Your useLayoutEffect runs every render because dep shortcutHandlers is a new object built fresh each time, so === always fails.
Move the value inside the hook body and depend on its simple inputs instead, or wrap it in useMemo / useCallback so it stays the same between renders.
(no-effect-with-fresh-deps)
🔇 Additional comments (16)
src-tauri/src/commands/settings.rs (1)
168-173: LGTM!Also applies to: 678-684, 687-693, 694-702, 705-717, 718-726, 1138-1149
src/hooks/useUpdateCheck.ts (1)
1-8: LGTM!Also applies to: 89-175, 210-212
src/lib/errors.ts (1)
21-36: LGTM!Also applies to: 61-78
tests/errors.test.ts (1)
1-61: LGTM!src/hooks/usePlaylistSources.ts (1)
293-315: LGTM!src-tauri/src/engine/remote_cache.rs (3)
155-172: Resolves prior TLS-bypass finding.
accept_invalid_certsis now threaded as an explicit parameter intodownload_playlist_to_file'sClientBuilder(line 172) instead of the previous unconditionaldanger_accept_invalid_certs(true). This is the exact location flagged in the earlier review comment and it is now fixed.Also applies to: 296-394
133-153: LGTM!
Usingerror.without_url()to build the error message avoids leaking credential-bearing query strings in logs, which resolves the credential-logging concern noted in the PR objectives.
423-593: LGTM!
Good coverage: deterministic cache naming, stale-temp cleanup selectivity, oversized-response rejection, and timeout handling with temp-file cleanup on failure are all exercised, now with theaccept_invalid_certsparameter explicitly set tofalse.src-tauri/src/engine/xtream.rs (2)
370-381: Resolves prior EXTINF-injection finding.Escaping tvg-id/tvg-logo/group-title via
parser::escape_extinf_valueand flattening the display name viaflatten_extinf_titlecloses the previously flagged gap where provider-supplied values could break#EXTINFattribute parsing or inject spurious lines. The new test exercises quotes, backslashes, and embedded newlines end-to-end.Also applies to: 469-474, 585-611
387-397: Resolves prior TLS-bypass finding.
accept_invalid_certsis now an explicit parameter threaded into bothfetch_xtream_playlist_via_json_api's andfetch_xtream_account_info'sClientBuilders, replacing the previous hard-codeddanger_accept_invalid_certs(true)flagged at these exact locations in the earlier review.Also applies to: 492-507
src-tauri/src/commands/playlist.rs (4)
26-30: LGTM!
420-430: Good fix: TLS bypass now gated behind an explicit setting.This correctly threads
accept_invalid_certsfrom app settings, defaulting tofalse(fail-closed) when noAppHandleis available. This resolves the previously flagged issue wheredanger_accept_invalid_certs(true)was applied unconditionally inremote_cache.rsandxtream.rs.
623-649: LGTM!
Theaccept_invalid_certsvalue is computed once per Xtream open and reused consistently across the parallel account-info fetch, the/get.phpdownload, and the JSON-API fallback, and the JSON-fallback cache write correctly moves tospawn_blocking.Also applies to: 676-758, 760-768, 938-945
538-591: 🩺 Stability & AvailabilityNo change needed.
parse_playlist_off_threadalready mapsJoinErrorfromspawn_blockingtoAppError::Other, and the progress callback only captures a clonedAppHandlefor the duration of the blocking parse.src-tauri/src/engine/stalker.rs (1)
459-464: LGTM!
Switching to the sharedparser::escape_extinf_value/flatten_extinf_titlehelpers removes duplicated escaping logic and keeps Stalker-generated EXTINF lines consistent with the Xtream and file-based parsing paths. The new test verifies quote/backslash/newline handling correctly.Also applies to: 604-628
src-tauri/src/engine/parser.rs (1)
13-27: LGTM!
escape_extinf_valuecorrectly escapes backslashes before quotes (avoiding double-escaping) and flattens line breaks to prevent EXTINF-line injection;flatten_extinf_titleintentionally leaves quotes/backslashes untouched since the display name is unquoted text after the metadata comma. The added test verifies the exact escaping order.Also applies to: 769-776
|
@codex review |
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
# Conflicts: # src/hooks/useStreamPlayer.ts
A full-codebase review pass (48 commits): correctness bugs, proxy hardening, performance work, and structural decomposition of the four oversized files. No feature changes.
Bugs fixed
useScan(scan events were processed twice under StrictMode remounts),useChromecast, and the App settings listener — all now use thePromise.all+ cancellation-guard pattern.player_api.php.setStateupdaters inChannelTablecould double-start playback on arrow-key navigation."Placeholder", silently degrading those results to Dead — channel status is now a typed enum end-to-end.Security/stability
Policy::none()and hop manually); previously a public upstream could 302 to127.0.0.1/169.254.169.254unchecked.kill_on_drop; the cast pump uses escalating reconnect backoff with a healthy-session threshold instead of hammering dead upstreams at 5 req/s; raw proxy request heads are read to the terminator under a deadline; app setup no longer blocks on the proxy bind.Performance
Arcs instead of deep-copying full playlists; retained per-channel diagnostics capped at 64 KiB; screenshot sidebar cache is LRU-capped; TS pacer scans payloads in place.Structure
playlist.rs(3.4k lines) split intoengine/{xtream,stalker,remote_cache}+commands/server_test;scan.rs's 1,050-lineexecute_scan_rundecomposed (24-param helper now takes a context struct);App.tsx3.2k → 1.7k lines viausePlaylistSources/useUpdateCheck/useMenuEventBridge/StartScreen/AppBanners;useStreamPlayerpolicy extracted tolib/playback.ts.RemuxState.child, legacylast_error_reason);AppErrorgainsValidation/Statevariants;tauri-plugin-mcppinned to its locked rev.Review notes
A local CodeRabbit preflight ran over the branch; genuine findings were fixed (diagnostics cap overshoot, JSON read caps, log redaction, request-head deadline, pump backoff threshold, banner a11y labels, two App effect races). Findings targeting pre-existing behavior that this branch only relocated (keychain credential storage, server-test probe semantics, M3U attribute escaping, semver prerelease ordering, LL-HLS tag coverage, DNS-rebinding TOCTOU pinning, module layout preferences) were deliberately left out of scope.
Validation
tscstrict — all green, zero warnings.Summary by CodeRabbit
New Features
Bug Fixes