Skip to content

WebAPI/WebUI: Add support for downloading completed torrent files#23743

Open
Piccirello wants to merge 2 commits intoqbittorrent:masterfrom
Piccirello:tom/download-file
Open

WebAPI/WebUI: Add support for downloading completed torrent files#23743
Piccirello wants to merge 2 commits intoqbittorrent:masterfrom
Piccirello:tom/download-file

Conversation

@Piccirello
Copy link
Member

This PR adds a new WebAPI endpoint for downloading individual torrent files directly from the WebUI. This allows users to retrieve completed files without needing a separate file server/access mechanism. It also supports streaming files directly to a media player, like VLC.

qBittorrent's HTTP server doesn't currently support the concept of streaming. Any data must be fully loaded into memory before it can be sent to the client. But this approach is impractical for large files. Now, qBittorrent's HTTP server additionally supports thread-based streaming. When a download is requested, the HTTP headers are sent on the main thread. The QTcpSocket is then moved to a dedicated worker thread that handles the file streaming synchronously, to avoid blocking. Once the download completes, the worker thread is cleaned up. This approach allows for a relatively simple implementation and reliable flow control via waitForBytesWritten(). (I also explored implementing this with async streaming but the code was much more complicated and I couldn't get flow control working correctly)

To keep memory usage low, file data is read in 256KB chunks with a 512KB socket buffer limit. The streaming thread blocks when the buffer is full, ensuring memory usage stays constant regardless of file size or transfer rate. We currently limit the server to 3 concurrent file downloads to bound total resource usage, but this is just an arbitrary limit.

Closes #15561.
Closes #15729.

@Piccirello Piccirello added this to the 5.2 milestone Jan 15, 2026
@Piccirello Piccirello requested review from a team January 15, 2026 05:58
@Piccirello Piccirello added WebUI WebUI-related issues/changes WebAPI WebAPI-related issues/changes labels Jan 15, 2026
Comment on lines 151 to +156
struct Response
{
ResponseStatus status;
HeaderMap headers;
QByteArray content;
std::optional<StreamingInfo> streaming;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR raises some big architectural questions around how we want to handle streaming. I opted for adding an optional streaming struct to the existing Response struct, to ensure that non-streaming requests aren't impacted in terms of performance or memory usage. There are other ways to do this, but this seemed like the best balance of being able to integrate streaming into our existing architecture with minimal changes (see src/base/http/connection.cpp).

@glassez
Copy link
Member

glassez commented Jan 15, 2026

@Piccirello
Unfortunately, I won't be able to review this PR in the near future for objective reasons (I don't have access to any computer, and I'm unlikely to be able to review such changes from my smartphone). Perhaps I can only make some preliminary general remarks. Here are the first ones:

  1. It's usually not a good idea to try to reuse some infrastructure that was originally designed to serve a simplified use case for something more complex.
  2. It's usually not a good idea to read files in parallel (unless it's about a single thread per each physical disk).

@Piccirello
Copy link
Member Author

  1. It's usually not a good idea to try to reuse some infrastructure that was originally designed to serve a simplified use case for something more complex.

I agree in the abstract, though I think this change preserves the simplicity well. Nevertheless if you'd prefer a different approach then I would appreciate a more specific suggestion when you have the time.

  1. It's usually not a good idea to read files in parallel (unless it's about a single thread per each physical disk).

This is true of spinning HDDs but generally does not apply to modern SSDs. SSDs handle parallelism well, especially in the small chunks that this code reads.

If this remains a concern we can limit the server to one concurrent stream, or make it configurable.

@Chocobo1
Copy link
Member

Chocobo1 commented Jan 17, 2026

It's usually not a good idea to try to reuse some infrastructure that was originally designed to serve a simplified use case for something more complex.

To be honest, the idea of fetching files over qbt webserver crossed my mind more than a few times before jagannatharjun presented his work. However over the time, I'm leaning more to delegate that work to some specific application that is designed to do that job well, instead of shoving it into the simple qbt webserver. I suppose there are a few reasons.
First is maintainability. Once users get accustomed to this new feature they will ask more of it. And where do we draw the line? Are we committed to fix all those bugs/issues related to the new feature? (especially when the feature isn't quite related to core torrent operations)
Second, having too much functionality into one process is not a robust design. It becomes hard to secure every function and make sure they work well. It is too easy to have some minor bug in a function and it became an exploit or let it crash the whole qbt app. I mean, In our scenario here, I would consider multi (independent) processes/apps being a better model. The code for each app could be kept small and easy (and extendable within each boundary). And we won't overload each app responsibility.
For example, I imagine we could specify/hardcoded some 3rd party app that is required for the 'file transfer' task and require the user to install it. Just like how we require python for the 'search engine' functionality. Or we can ask user to provide a commandline script to start a 3rd party webserver, similar to the 'Run external program' functionality.

@glassez
Copy link
Member

glassez commented Jan 17, 2026

I generally agree with the first part of the @Chocobo1 comment above.
But I disapprove of any strict requirements/dependencies with other software in this matter. All we could have was to provide documentation with an example of how someone could configure other related software to access files on qBittorrent host (just as we provide examples of reverse proxy configurations, etc.). Similarly, someone could provide docker containers containing both qBittorrent, as well as other related software configured to provide access to the directory where it saves files.

@Piccirello
Copy link
Member Author

First is maintainability. Once users get accustomed to this new feature they will ask more of it. And where do we draw the line? Are we committed to fix all those bugs/issues related to the new feature? (especially when the feature isn't quite related to core torrent operations)

This is a good point to bring up. I think this question describes the WebUI in general. There was a WebUI implementation with a limited feature set, and over the last 8 years myself and other devs have added more and more functionality. Technically none of this is related to core torrent operations either, but a dedicated group of individuals focuses on it. We (@Chocobo1 included) fix bugs and address feature requests that we think are reasonable. It doesn't mean we have to build everything users ask for.

The tight integration of this feature directly in qBittorrent makes for a much better experience than an equivalent, external piece of software could provide. It works especially well on remote seedboxes. For that reason, deluge supports this via the "deluge-streaming" plugin and ruTorrent via the File Manager plugin and MediaStream plugin.

@Piccirello
Copy link
Member Author

I'd really love to get some early feedback on this PR so that it doesn't grow too stale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

WebAPI WebAPI-related issues/changes WebUI WebUI-related issues/changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow retrieval of downloaded files via webapi Stream videos from VLC

3 participants