Skip to content

feat(create): Add --piece-size flags #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ mkbrr create path/to/file -t https://example-tracker.com/announce --exclude "*.n

# Create a torrent including only specific file patterns (comma-separated)
mkbrr create path/to/video-folder -t https://example-tracker.com/announce --include "*.mkv,*.mp4"

# Create with a specific piece size (e.g., 4 MiB) - alternative to -l/--piece-length
mkbrr create path/to/file -t https://example-tracker.com/announce --piece-size 4MiB

# Create limiting max piece size (e.g., 8 MiB) - alternative to -m/--max-piece-length
mkbrr create path/to/file -t https://example-tracker.com/announce --max-piece-size 8MiB

# Note on --piece-size:
# The specified size is rounded up to the nearest power of 2 (e.g., 14k becomes 16KiB).
# The final piece size is then clamped between 64 KiB (minimum) and 128 MiB (maximum).
# So, --piece-size 14k will result in 64 KiB pieces due to the minimum limit.
```

> [!NOTE]
Expand Down Expand Up @@ -450,4 +461,4 @@ Summary

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

See [LICENSE](LICENSE) for the full license text.
See [LICENSE](LICENSE) for the full license text.
121 changes: 84 additions & 37 deletions cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,29 @@ import (
)

var (
trackerURL string
isPrivate bool
comment string
pieceLengthExp *uint // for 2^n piece length, nil means automatic
maxPieceLengthExp *uint // for maximum 2^n piece length, nil means no limit
outputPath string
webSeeds []string
noDate bool
noCreator bool
source string
verbose bool
batchFile string
presetName string
presetFile string
entropy bool
quiet bool
skipPrefix bool
excludePatterns []string
includePatterns []string
trackerURL string
isPrivate bool
comment string
pieceSizeStr string
maxPieceSizeStr string
outputPath string
webSeeds []string
noDate bool
noCreator bool
source string
verbose bool
batchFile string
presetName string
presetFile string
entropy bool
quiet bool
skipPrefix bool
excludePatterns []string
includePatterns []string
)

var defaultPieceLength, defaultMaxPieceLength uint

var createCmd = &cobra.Command{
Use: "create [path]",
Short: "Create a new torrent file",
Expand Down Expand Up @@ -83,18 +85,10 @@ func init() {

// piece length flag allows setting a fixed piece size of 2^n bytes
// if not specified, piece length is calculated automatically based on total size
var defaultPieceLength, defaultMaxPieceLength uint
createCmd.Flags().UintVarP(&defaultPieceLength, "piece-length", "l", 0, "set piece length to 2^n bytes (16-27, automatic if not specified)")
createCmd.Flags().UintVarP(&defaultMaxPieceLength, "max-piece-length", "m", 0, "limit maximum piece length to 2^n bytes (16-27, unlimited if not specified)")
createCmd.PreRun = func(cmd *cobra.Command, args []string) {
if cmd.Flags().Changed("piece-length") {
pieceLengthExp = &defaultPieceLength
}
if cmd.Flags().Changed("max-piece-length") {
maxPieceLengthExp = &defaultMaxPieceLength
}
}

createCmd.Flags().UintVarP(&defaultPieceLength, "piece-length", "l", 0, "set piece length to 2^n bytes (16-27, alternative to --piece-size)")
createCmd.Flags().StringVar(&pieceSizeStr, "piece-size", "", "set piece size (e.g., \"4M\", \"8192k\", alternative to --piece-length). Value is rounded up to the nearest power of 2 and clamped between 64KiB-128MiB.")
createCmd.Flags().UintVarP(&defaultMaxPieceLength, "max-piece-length", "m", 0, "limit maximum piece length to 2^n bytes (16-27, alternative to --max-piece-size)")
createCmd.Flags().StringVar(&maxPieceSizeStr, "max-piece-size", "", "limit maximum piece size (e.g., \"16M\", \"16384k\", alternative to --max-piece-length)")
createCmd.Flags().StringVarP(&outputPath, "output", "o", "", "set output path (default: <name>.torrent)")
createCmd.Flags().StringVarP(&source, "source", "s", "", "add source string")
createCmd.Flags().BoolVarP(&noDate, "no-date", "d", false, "don't write creation date")
Expand All @@ -117,6 +111,43 @@ Flags:
}

func runCreate(cmd *cobra.Command, args []string) error {
pieceLengthChanged := cmd.Flags().Changed("piece-length")
pieceSizeChanged := cmd.Flags().Changed("piece-size")
maxPieceLengthChanged := cmd.Flags().Changed("max-piece-length")
maxPieceSizeChanged := cmd.Flags().Changed("max-piece-size")

if pieceLengthChanged && pieceSizeChanged {
return fmt.Errorf("cannot use both --piece-length and --piece-size")
}
if maxPieceLengthChanged && maxPieceSizeChanged {
return fmt.Errorf("cannot use both --max-piece-length and --max-piece-size")
}

var userPieceLengthExp *uint
var userMaxPieceLengthExp *uint
var userPieceSize *uint64
var userMaxPieceSize *uint64

if pieceLengthChanged {
userPieceLengthExp = &defaultPieceLength
} else if pieceSizeChanged {
parsedSize, err := torrent.ParseSize(pieceSizeStr)
if err != nil {
return fmt.Errorf("invalid --piece-size value: %w", err)
}
userPieceSize = &parsedSize
}

if maxPieceLengthChanged {
userMaxPieceLengthExp = &defaultMaxPieceLength
} else if maxPieceSizeChanged {
parsedMaxSize, err := torrent.ParseSize(maxPieceSizeStr)
if err != nil {
return fmt.Errorf("invalid --max-piece-size value: %w", err)
}
userMaxPieceSize = &parsedMaxSize
}

if cpuprofile, _ := cmd.Flags().GetString("cpuprofile"); cpuprofile != "" {
f, err := os.Create(cpuprofile)
if err != nil {
Expand Down Expand Up @@ -259,12 +290,26 @@ func runCreate(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("comment") {
opts.Comment = comment
}
if cmd.Flags().Changed("piece-length") {
opts.PieceLengthExp = pieceLengthExp
opts.PieceLengthExp = userPieceLengthExp
opts.MaxPieceLength = userMaxPieceLengthExp
opts.PieceSize = userPieceSize
opts.MaxPieceSize = userMaxPieceSize

if pieceLengthChanged || pieceSizeChanged {
if pieceLengthChanged {
opts.PieceSize = nil
} else {
opts.PieceLengthExp = nil
}
}
if cmd.Flags().Changed("max-piece-length") {
opts.MaxPieceLength = maxPieceLengthExp
if maxPieceLengthChanged || maxPieceSizeChanged {
if maxPieceLengthChanged {
opts.MaxPieceSize = nil
} else {
opts.MaxPieceLength = nil
}
}

if cmd.Flags().Changed("source") {
opts.Source = source
}
Expand Down Expand Up @@ -298,8 +343,10 @@ func runCreate(cmd *cobra.Command, args []string) error {
WebSeeds: webSeeds,
IsPrivate: isPrivate,
Comment: comment,
PieceLengthExp: pieceLengthExp,
MaxPieceLength: maxPieceLengthExp,
PieceLengthExp: userPieceLengthExp,
MaxPieceLength: userMaxPieceLengthExp,
PieceSize: userPieceSize,
MaxPieceSize: userMaxPieceSize,
Source: source,
NoDate: noDate,
NoCreator: noCreator,
Expand Down
2 changes: 1 addition & 1 deletion internal/torrent/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ func (j *BatchJob) ToCreateOptions(verbose bool, quiet bool, version string) Cre
type BatchResult struct {
Error error
Info *TorrentInfo
Job BatchJob
Trackers []string
Job BatchJob
Success bool
}

Expand Down
Loading