hardlinktree

package
v1.31.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 7, 2026 License: GPL-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package hardlinktree provides utilities for creating hardlink trees that mirror torrent file layouts for cross-seeding.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoMatchingFile  = &LinkPlanError{Kind: LinkPlanErrorNoMatchingFile}
	ErrNoAvailableFile = &LinkPlanError{Kind: LinkPlanErrorNoAvailableFile}
	ErrCouldNotMatch   = &LinkPlanError{Kind: LinkPlanErrorCouldNotMatch}
)

Functions

func HasCommonRootFolder

func HasCommonRootFolder(files []TorrentFile) bool

HasCommonRootFolder checks if all files in the torrent share a common top-level directory. Returns true if a single root folder exists.

Examples:

  • ["Movie/video.mkv", "Movie/subs.srt"] → true (common root "Movie")
  • ["video.mkv", "subs.srt"] → false (files at root, no folder)
  • ["Movie/video.mkv", "Other/subs.srt"] → false (different root folders)
  • ["video.mkv"] → false (single file at root)

func MkdirAllTracked added in v1.26.0

func MkdirAllTracked(dir string) ([]string, error)

MkdirAllTracked creates dir and any missing ancestors, like os.MkdirAll, and returns the directories it actually created (shallowest first). Pre-existing directories are not returned, so rollback never removes a directory another attempt made.

Types

type ContentLayout

type ContentLayout string

ContentLayout describes how qBittorrent organizes torrent content.

const (
	LayoutOriginal    ContentLayout = "Original"    // Files placed as-is from torrent
	LayoutSubfolder   ContentLayout = "Subfolder"   // All files wrapped in a folder named after torrent
	LayoutNoSubfolder ContentLayout = "NoSubfolder" // Top-level folder stripped from multi-file torrent
)

type Created added in v1.26.0

type Created struct {
	Files []string
	Dirs  []string
}

Created records the files and directories a single Create call actually made on disk. Rollback removes exactly those and nothing else: destinations can be shared between torrents, and a plan-wide cleanup would delete links belonging to a sibling torrent that succeeded (discussion #2282).

func Create

func Create(plan *TreePlan) (*Created, error)

Create materializes the hardlink tree plan on disk. Creates necessary directories and hardlinks files from source to target paths. On failure, attempts best-effort rollback of created files and returns a nil handle.

On success, returns a handle recording what this call created. Targets that already were hardlinks to the source are skipped and NOT recorded: they belong to whoever created them first.

func (*Created) Rollback added in v1.26.0

func (c *Created) Rollback() error

Rollback removes the files and directories recorded in c. Best-effort: continues even if some removals fail. Directories are only removed when empty. Safe to call on a nil or empty handle.

type ExistingFile

type ExistingFile struct {
	AbsPath string // Absolute path on disk
	RelPath string // Relative path within the torrent structure
	Size    int64  // File size in bytes
}

ExistingFile represents a file that already exists on disk.

type FilePlan

type FilePlan struct {
	SourcePath string // Absolute path to existing file on disk
	TargetPath string // Absolute path where hardlink should be created
}

FilePlan describes a single hardlink to create.

type LinkPlanError added in v1.16.0

type LinkPlanError struct {
	Kind LinkPlanErrorKind
	File string
}

LinkPlanError describes a file-specific plan-building failure.

func (*LinkPlanError) Error added in v1.16.0

func (e *LinkPlanError) Error() string

func (*LinkPlanError) Is added in v1.16.0

func (e *LinkPlanError) Is(target error) bool

type LinkPlanErrorKind added in v1.16.0

type LinkPlanErrorKind string

LinkPlanErrorKind identifies a file-matching failure when building a plan.

const (
	LinkPlanErrorNoMatchingFile  LinkPlanErrorKind = "no_matching_file"
	LinkPlanErrorNoAvailableFile LinkPlanErrorKind = "no_available_file"
	LinkPlanErrorCouldNotMatch   LinkPlanErrorKind = "could_not_match"
)

type TorrentFile

type TorrentFile struct {
	Path string // Relative path within the torrent (e.g., "Folder/file.mkv")
	Size int64  // File size in bytes
}

TorrentFile represents a file in a torrent.

type TreePlan

type TreePlan struct {
	RootDir string     // Root directory for the hardlink tree
	Files   []FilePlan // Files to hardlink
}

TreePlan describes the complete hardlink tree to create.

func BuildPlan

func BuildPlan(
	candidateFiles []TorrentFile,
	existingFiles []ExistingFile,
	layout ContentLayout,
	torrentName string,
	destDir string,
) (*TreePlan, error)

BuildPlan creates a hardlink tree plan that maps existing files to the incoming torrent's layout.

Parameters:

  • candidateFiles: Files as they appear in the INCOMING torrent (target layout)
  • existingFiles: Files from the MATCHED torrent that already exist on disk
  • layout: How qBittorrent will organize the torrent (from instance preferences)
  • torrentName: Name of the incoming torrent (used for Subfolder layout)
  • destDir: Base directory for the hardlink tree

The resulting tree will match what qBittorrent expects when adding the torrent.

func BuildSingleFilePlan added in v1.28.0

func BuildSingleFilePlan(rootDir, relativePath, sourcePath string) (*TreePlan, error)

BuildSingleFilePlan creates an exact-path plan for one already-paired torrent file. It rejects unsafe or non-canonical slash paths, performs no I/O, and never performs fuzzy matching or conflict renaming.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL