Skip to content

feat(service_qbittorrent): add execution logs tab and fix filter drawer gesture scoping - #159

Merged
retransmit merged 8 commits into
retransmit:developmentfrom
lxBlazarxl:development
Sep 17, 2026
Merged

retransmit merged 8 commits into
retransmit:developmentfrom
lxBlazarxl:development

Conversation

@lxBlazarxl

Copy link
Copy Markdown
Collaborator

Summary

This PR adds an Execution Logs tab to service_qbittorrent and fixes an issue where the right-edge drawer gesture remained active on non-home tabs.

The bottom navigation bar order is now:

  1. Home (torrents list)
  2. Settings (system, behavior, speed, bittorrent, etc.)
  3. Logs (application execution logs)

Key Changes

1. Gesture Scoping Fix

  • Previously, QbittorrentHome unconditionally attached QbittorrentFilterDrawer as the endDrawer on the outer Scaffold with endDrawerEnableOpenDragGesture: true. Swiping right-to-left while in the Settings tab opened the torrent filter drawer.
  • endDrawer and endDrawerEnableOpenDragGesture are now strictly enabled only when on the Home tab (currentIndex == 0), preventing unwanted drawer opens on Settings and Logs.

2. Logs API Integration & Model

  • QbitLogEntry: Represents log items from GET /api/v2/log/main (id, message, timestamp, type), with QbitLogLevel enum (normal, info, warning, critical) and local time formatting (HH:mm:ss).
  • QbittorrentClient.getLogs: Calls api/v2/log/main supporting severity filters (normal, info, warning, critical) and last_known_id.
  • qbitLogsProvider: Auto-disposing Riverpod provider fetching logs for a given instance.

3. Logs Tab UI

  • Located at tab index 2 (Home -> Settings -> Logs).
  • Header Actions:
    • Drawer menu toggle (Icons.menu).
    • Search toggle with inline filter input.
    • "Copy visible logs" action (Icons.copy_all_outlined).
    • Manual refresh action (Icons.refresh).
  • Severity Filter Chips: Horizontal filter chips for All, Normal, Info, Warning, and Critical.
  • Card Styling: Log items are styled to match _TorrentTile from the home screen:
    • Material surface card (cs.surfaceContainerHigh) with BorderRadius.circular(20) and Insets.md padding.
    • 44x44 icon container with borderRadius: BorderRadius.circular(14) using semantic level container colors.
    • Severity pill (_StatePill) with 15% opacity tint and BorderRadius.circular(20).
    • Monospace timestamp and tap-to-copy functionality.
  • Interactions: Integrated with EasyRefresh pull-to-refresh and responds to bottom nav tap scroll-to-top (qbitHomeScrollToTopProvider).

lxBlazarxl and others added 8 commits September 17, 2026 13:43
The tabs sit in an IndexedStack, which builds every tab when the screen
opens, so the Logs tab fetched qBittorrent's whole log on each visit to the
screen, whether or not anyone looked at it. Seen through a proxy in front
of a live qBittorrent: opening the screen on the Home tab requested
/api/v2/log/main. qBittorrent keeps up to 20,000 entries, and a full log
came to about 2 MB of JSON.

The Logs tab is now built the first time it is selected and kept after
that, so its filter, search and scroll position are still there when you
come back to it.
qBittorrent keeps up to 20,000 log entries. With a live server holding
that many, Copy put 1.3 MB of text on the clipboard, Android refused it
with a TransactionTooLargeException, and the app still said "Copied 20000
log entries to clipboard". Nothing had been copied, so pasting gave
whatever was on the clipboard before.

Copy now takes the newest entries that fit in 100,000 characters, keeps
them in the order they happened, and says when it left older ones out.
Both copy actions wait for the clipboard and say so when the platform
refuses. The button's tooltip no longer promises all logs, since it
copies what the filter and search leave.
The timestamp comment called milliseconds the format and seconds the one
older versions send. It is the other way round: qBittorrent's API
documentation says the log switched from milliseconds to seconds in 4.5.0,
and a live 5.2.3 sends seconds. The code already reads both, but a reader
trusting the comment could drop the branch that every current server needs.
…utes

The Advanced settings pickers for the network interface and the address to
bind to never listed anything. The client asked for
app/networkInterfacesList and app/networkInterfaceAddressesList, which
qBittorrent does not have: on a live 5.2.3 both answer 404, and the routes
are networkInterfaceList and networkInterfaceAddressList. The client caught
the failure and returned an empty list, so the pickers only offered their
built-in choices and nothing looked wrong.

The address route also needs its iface parameter even when it is empty.
Without it qBittorrent answers 400, and an empty value is how every address
is asked for, which is what the default "Any interface" needs. It is now
always sent.
@retransmit

Copy link
Copy Markdown
Owner

Nice one, the Logs tab is a good add. Drawer fix works on device too, and reading both seconds and milliseconds was smart, since qBittorrent switched to seconds in 4.5.0.

I pushed four commits. Most of it only showed up once I pointed it at a log with 20,000 entries in it:

  • The tab was loading the whole log every time the qBittorrent screen opened, even if you never tapped Logs. IndexedStack builds all the tabs up front, so /api/v2/log/main was going out while you were still on Home. That's about 2 MB of JSON on a busy server. It now loads the first time you open the tab and sticks around after.
  • Copy said "Copied 20000 log entries to clipboard" and actually copied nothing. Android refuses clipboard writes past roughly a megabyte and the full log came to 1.3 MB. It now copies the newest entries that fit in 100k characters, tells you when it left older ones out, and owns up when the clipboard refuses.
  • The timestamp comment has seconds and milliseconds the wrong way round. Seconds is the current format, milliseconds is the old one. The code already read both, so only the comment changed.
  • Not from this PR, but I was already in the file: the network interface pickers in Advanced were always empty. We ask for networkInterfacesList and networkInterfaceAddressesList, and qBittorrent calls them networkInterfaceList and networkInterfaceAddressList, so both 404 and we swallowed the error. The address one also 400s unless iface is sent, where empty means "all", which is what Any interface needs.

Two I left for you:

  • With the log search open, back goes to the Home tab instead of closing the search. On the torrents tab back clears the search first.
  • Logs only refresh when you ask. last_known_id would make a live tail cheap if you want one.

Analyzer is clean, tests pass, release build still reproducible.

@retransmit
retransmit merged commit 46e6c8e into retransmit:development Sep 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants