lure/internal/dl/torrent.go

91 lines
1.9 KiB
Go

package dl
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
)
var (
urlMatchRegex = regexp.MustCompile(`(magnet|torrent\+https?):.*`)
ErrAria2NotFound = errors.New("aria2 must be installed for torrent functionality")
ErrDestinationEmpty = errors.New("the destination directory is empty")
)
type TorrentDownloader struct{}
// Name always returns "file"
func (TorrentDownloader) Name() string {
return "torrent"
}
// MatchURL returns true if the URL is a magnet link
// or an http(s) link with a "torrent+" prefix
func (TorrentDownloader) MatchURL(u string) bool {
return urlMatchRegex.MatchString(u)
}
// Download downloads a file over the BitTorrent protocol.
func (TorrentDownloader) Download(opts Options) (Type, string, error) {
aria2Path, err := exec.LookPath("aria2c")
if err != nil {
return 0, "", ErrAria2NotFound
}
opts.URL = strings.TrimPrefix(opts.URL, "torrent+")
cmd := exec.Command(aria2Path, "--summary-interval=0", "--log-level=warn", "--seed-time=0", "--dir="+opts.Destination, opts.URL)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return 0, "", fmt.Errorf("aria2c returned an error: %w", err)
}
err = removeTorrentFiles(opts.Destination)
if err != nil {
return 0, "", err
}
return determineType(opts.Destination)
}
func removeTorrentFiles(path string) error {
filePaths, err := filepath.Glob(filepath.Join(path, "*.torrent"))
if err != nil {
return err
}
for _, filePath := range filePaths {
err = os.Remove(filePath)
if err != nil {
return err
}
}
return nil
}
func determineType(path string) (Type, string, error) {
files, err := os.ReadDir(path)
if err != nil {
return 0, "", err
}
if len(files) > 1 {
return TypeDir, "", nil
} else if len(files) == 1 {
if files[0].IsDir() {
return TypeDir, files[0].Name(), nil
} else {
return TypeFile, files[0].Name(), nil
}
}
return 0, "", ErrDestinationEmpty
}