Skip to content

Add optional hash-based directories for torrent payloads - #24742

Open
gl00ten wants to merge 4 commits into
qbittorrent:masterfrom
gl00ten:top-level-payload-uniqueness
Open

Add optional hash-based directories for torrent payloads #24742
gl00ten wants to merge 4 commits into
qbittorrent:masterfrom
gl00ten:top-level-payload-uniqueness

Conversation

@gl00ten

@gl00ten gl00ten commented Jul 26, 2026

Copy link
Copy Markdown

Fixes:
#12842

Summary

This PR adds an opt-in option to give each torrent payload a deterministic top-level directory based on the torrent hash.

For example:

My Torrent [qb-1a2b3c4d5e6f]/

The goal is to prevent unrelated torrents with the same payload name from writing into the same directory.

Screenshots:

New option in settings:
image

New item in right click menu:
image

New style for directories. Guarantees uniqueness and sameness across sessions.
image

New option to preserve original name (for cross seeding) and what not:
image

Behaviour

When hash naming is enabled:

  • New torrents receive a top-level directory with a short hash suffix.
  • Single-file torrents are also placed inside their own hashed directory.
  • The generated name is deterministic and remains the same when the torrent is restored.
  • Users can preserve the original payload name for individual torrents when intentional path sharing or cross-seeding is required.

Existing torrents can be converted through the context-menu action.

If the target hashed directory already exists, qBittorrent warns the user that the entire directory will be permanently deleted and replaced. Nothing is deleted unless the user confirms.

The existing directory is not merged and no alternative names such as (1) are generated.

Motivation

Two torrents can have the same top-level payload name while containing different data. When both use the same save location, their files may overlap or overwrite one another.

Adding a deterministic hash suffix gives every torrent a unique and predictable payload directory while keeping the original name recognisable.

(personally, I got tired of IO errors on torrents with the same name)

Testing

I manually tested:

  • Adding and downloading single-file torrents.
  • Converting existing single-file torrents.
  • Filename collisions involving single-file torrents.
  • Filename collisions involving multi-file torrents.
  • The warning shown when the target hashed directory already exists.
  • Confirming the warning deletes and replaces the existing hashed directory.
  • Cancelling the warning leaves the existing directory unchanged.

Unit tests were also added for the payload-name generation logic, including single-file, multi-file, rootless, Unicode, long-name and idempotence cases.

Edit:

This also fixes the magnet-versus-.torrent naming inconsistency. Magnet links may initially have only a display name and incomplete metadata, but once the metadata is available, qBittorrent uses the torrent’s canonical name and torrent ID to generate the payload path. As a result, adding the same torrent through a magnet link or a .torrent file should produce the same deterministic hashed folder name instead of creating different directories based on the magnet display name. The only unrelated edge case is duplicate detection between unresolved v1-only and v2-only magnets for the same hybrid torrent, since qBittorrent cannot know they refer to the same torrent until metadata is received.

@HanabishiRecca

Copy link
Copy Markdown
Contributor

I like the idea, but don't really like the implementation.
It feels like to me it should be just another option for torrent content layout. So it would be something like

  • Original
  • Create subfolder
  • Create unique subfolder
  • Don't create subfolder

Also maybe just appending full hash but without the qb- prefix would be better?

@glassez

glassez commented Jul 30, 2026

Copy link
Copy Markdown
Member

It feels like to me it should be just another option for torrent content layout. So it would be something like

  • Original
  • Create subfolder
  • Create unique subfolder
  • Don't create subfolder

This idea has also crossed my mind.

@gl00ten

gl00ten commented Aug 2, 2026

Copy link
Copy Markdown
Author

@HanabishiRecca @glassez

Sure, I can move it into the existing content-layout dropdown.

The qb- was meant to show that qBittorrent added the suffix, but removing it would make the scheme easier for other clients to adopt.

My only concern with using the full hash is path length: v1 adds 40 characters and v2 up to 64, which may cause problems with long names, especially on Windows.

I think 12 hex characters is more than enough in practice, with roughly a 3.55e-15 (1 in trillions) collision probability. And then only for torrents with the same payload name.

I also tested it with cross-seeding, and it worked correctly:

#12842

When enabled, newly added torrents are stored under a stable top-level
directory named from the torrent and a short torrent-ID hash (for
example "Show [qb-a19f83c275d1]"). This avoids same-name path
collisions without scanning other torrents.

Existing torrents are unchanged until the user applies the conversion
from the transfer list. If the target hash directory already exists,
it is replaced entirely after confirmation. Per-torrent preserve
original name remains available for intentional cross-seeding.

Closes qbittorrent#12842
@HanabishiRecca

HanabishiRecca commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

My only concern with using the full hash is path length: v1 adds 40 characters and v2 up to 64, which may cause problems with long names, especially on Windows.

qBittorrent enables long paths on Windows (allows up to 10k path length), so it shouldn't be a problem. The problem could be with the dir name itself though.

I think 12 hex characters is more than enough in practice

Maybe. But either way you need to ensure that the combined name is shorter than 255 chars/bytes depending on a platform, and truncate torrent names somehow if necessary.
As in

#ifdef Q_OS_WIN
// Windows restricts file names to 255 characters
if (name.length() > 255)
return false;

#else
// Non-Windows systems limit file name lengths to 255 bytes in UTF-8 encoding
if (name.toUtf8().length() > 255)
return false;

Or maybe keeping only hashes as names is also an option.

Fold hash naming into TorrentContentLayout::UniqueSubfolder, always keep
the full 12-hex tag (truncate the name from the end to fit platform
filename limits), and reduce right-click migrate to rename-if-free only.
@gl00ten
gl00ten force-pushed the top-level-payload-uniqueness branch from 9ff82ec to ceb7aa1 Compare August 2, 2026 14:55
@stalkerok

Copy link
Copy Markdown
Member

gl00ten added 2 commits August 6, 2026 00:30
On convert, if the destination unique folder is already present, ask for
confirmation then proceed with normal per-file renames (matching paths
may be overwritten; unrelated files stay). Do not delete the destination
directory. Layout is still set only after all renames succeed.
Refuse conversion when any destination file path already exists (libtorrent
may not overwrite). Empty unique directories and unrelated files are fine.
Partial retries map under the unique root without double-wrapping. Drop
setName on success and remove unreleased append_hash resume compatibility.
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.

4 participants