magnet-to-torrent/main.go

95 lines
2.1 KiB
Go
Raw Normal View History

2025-04-25 13:53:27 -04:00
package main
import (
2025-05-04 13:30:55 -04:00
"context"
"flag"
2025-04-25 13:53:27 -04:00
"fmt"
"log"
"os"
2025-05-04 13:30:55 -04:00
"path/filepath"
"regexp"
2025-04-25 13:53:27 -04:00
"time"
"github.com/anacrolix/torrent"
)
2025-05-04 13:30:55 -04:00
var invalidFilenameChars = regexp.MustCompile(`[<>:"/\\|?*\x00-\x1F]`)
// sanitizeFilename replaces invalid filesystem characters with underscores.
func sanitizeFilename(name string) string {
return invalidFilenameChars.ReplaceAllString(name, "_")
}
2025-04-25 13:53:27 -04:00
func main() {
2025-05-04 13:30:55 -04:00
// Command-line flags
output := flag.String("o", "", "Output .torrent filename (optional)")
timeout := flag.Duration("timeout", 5*time.Minute, "Timeout for metadata retrieval")
upload := flag.Bool("upload", false, "Allow uploading (seeding) after metadata retrieval")
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] magnet_link\nOptions:\n", os.Args[0])
flag.PrintDefaults()
2025-04-25 13:53:27 -04:00
}
2025-05-04 13:30:55 -04:00
flag.Parse()
2025-04-25 13:53:27 -04:00
2025-05-04 13:30:55 -04:00
if flag.NArg() < 1 {
flag.Usage()
os.Exit(1)
2025-04-25 13:53:27 -04:00
}
2025-05-04 13:30:55 -04:00
magnetLink := flag.Arg(0)
2025-04-25 13:53:27 -04:00
2025-05-04 13:30:55 -04:00
// Configure torrent client
2025-04-25 13:53:27 -04:00
clientConfig := torrent.NewDefaultClientConfig()
clientConfig.DataDir = os.TempDir()
2025-05-04 13:30:55 -04:00
clientConfig.NoUpload = !*upload
2025-04-25 13:53:27 -04:00
client, err := torrent.NewClient(clientConfig)
if err != nil {
log.Fatalf("Error creating torrent client: %v", err)
}
defer client.Close()
2025-05-04 13:30:55 -04:00
// Add magnet link
2025-04-25 13:53:27 -04:00
t, err := client.AddMagnet(magnetLink)
if err != nil {
log.Fatalf("Error adding magnet link: %v", err)
}
2025-05-04 13:30:55 -04:00
// Wait for metadata with timeout
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
defer cancel()
select {
case <-t.GotInfo():
// Metadata received
case <-ctx.Done():
log.Fatalf("Timeout waiting for metadata after %v", *timeout)
}
2025-04-25 13:53:27 -04:00
info := t.Metainfo()
2025-05-04 13:30:55 -04:00
// Determine output filename
outFile := *output
if outFile == "" {
base := sanitizeFilename(t.Name())
outFile = base + ".torrent"
2025-04-25 13:53:27 -04:00
}
2025-05-04 13:30:55 -04:00
outFile = filepath.Clean(outFile)
2025-04-25 13:53:27 -04:00
2025-05-04 13:30:55 -04:00
// Write .torrent file
f, err := os.Create(outFile)
2025-04-25 13:53:27 -04:00
if err != nil {
2025-05-04 13:30:55 -04:00
log.Fatalf("Error creating file %s: %v", outFile, err)
2025-04-25 13:53:27 -04:00
}
defer f.Close()
2025-05-04 13:30:55 -04:00
if err := info.Write(f); err != nil {
2025-04-25 13:53:27 -04:00
log.Fatalf("Error writing torrent file: %v", err)
}
2025-05-04 13:30:55 -04:00
fmt.Printf("Torrent metadata saved to %s\n", outFile)
2025-04-25 13:53:27 -04:00
2025-05-04 13:30:55 -04:00
// Drop the torrent handle; client will close via defer
2025-04-25 13:53:27 -04:00
t.Drop()
}