bt2qbt/internal/transfer/resumeHandle.go

167 lines
5.3 KiB
Go
Raw Normal View History

Full refactor. Introduce tests (#35) * start refactor * default golang loyaout separate qbittorrent and torrent structures * stage * move flags handle to module and cover it with tests change handle sep with path.join * ugly remove unneeded fields from resume.dat * use path joing prepare for resume structs * Introduce subtests * fix behavior * Move to structures instead of using interfaces * Make main cmd clean, decomposite functions * Make main cmd clean, decomposite functions * Detect torrents functions and tests * Fixes * Fixes * New tests for content layout and file paths reformat code for project change libtorrent version append torrent v2 schema in torrent structs * filepath helper module fix some tests * fileHelpers new functions and tests * new function with cut last part with tests * function description and small fix * rename some variables and structures * beautiful tests handle for savePaths starting refactor savepaths * Refactor HandleSavePath func. Cover with tests Move replace field creation to handle and assign pointer, instead of generate each time with handle resumeItem. Moved opts to transferStructure. * fix some tests * HandleSavePaths covered with tests and little bit fixed * Another one additional test for fully function cover * check for tests * Remove PiecePriority * Refactored HandlePieces functions Removed unused functions * Rename variables. Introduced new tests * Append tests * Rename test funcs * New tests * New tests * move modules * introduce Makefile * Adapt fileHelpers for both windows and linux. Linux tests passed Use slashed paths for search torrents * remove test template * test fileset * Makefile. Version introduce * Fix darwin i386 version build * Bug fix with mappedfiles Need more tests and append new functionality. * tests for HandleSavePaths with absoulte paths in mapped files * additional tests with fileshare * all tests OK * Fix torrent state. Added tests * fixes after code inspection * Magnet link support. Tests * README.md update Co-authored-by: Alexey Kostin <a.kostin@corp.mail.ru>
2022-03-30 01:30:29 +02:00
package transfer
import (
"fmt"
"github.com/rumanzo/bt2qbt/internal/options"
"github.com/rumanzo/bt2qbt/pkg/fileHelpers"
"github.com/rumanzo/bt2qbt/pkg/helpers"
"github.com/rumanzo/bt2qbt/pkg/torrentStructures"
"github.com/rumanzo/bt2qbt/pkg/utorrentStructs"
"log"
"os"
"path/filepath"
"runtime"
"runtime/debug"
"strings"
"sync"
)
func HandleResumeItem(key string, transferStruct *TransferStructure, chans *Channels, wg *sync.WaitGroup) error {
//panic recover
defer wg.Done()
defer func() {
<-chans.BoundedChannel
}()
defer func() {
if r := recover(); r != nil {
chans.ErrChannel <- fmt.Sprintf(
"Panic while processing torrent %v:\n======\nReason: %v.\nText panic:\n%v\n======",
key, r, string(debug.Stack()))
}
}()
var err error
HandleTorrentFilePath(transferStruct, key)
err = FindTorrentFile(transferStruct)
if err != nil {
chans.ErrChannel <- err.Error()
return err
}
// struct for work with
err = helpers.DecodeTorrentFile(transferStruct.TorrentFilePath, transferStruct.TorrentFile)
if err != nil {
chans.ErrChannel <- fmt.Sprintf("Can't decode torrent file %v for %v", transferStruct.TorrentFilePath, key)
return err
}
// because hash of info very important it will be better to use interface for get hash
if !strings.HasPrefix(key, "magnet:?") {
err = helpers.DecodeTorrentFile(transferStruct.TorrentFilePath, &transferStruct.TorrentFileRaw)
if err != nil {
chans.ErrChannel <- fmt.Sprintf("Can't decode torrent file %v for %v", transferStruct.TorrentFilePath, key)
return err
}
} else {
transferStruct.Magnet = true
transferStruct.TorrentFile = &torrentStructures.Torrent{
Info: &torrentStructures.TorrentInfo{},
}
}
transferStruct.HandleStructures()
newBaseName := transferStruct.GetHash()
if err = helpers.EncodeTorrentFile(filepath.Join(transferStruct.Opts.QBitDir, newBaseName+".fastresume"), transferStruct.Fastresume); err != nil {
chans.ErrChannel <- fmt.Sprintf("Can't create qBittorrent fastresume file %v. With error: %v", filepath.Join(transferStruct.Opts.QBitDir, newBaseName+".fastresume"), err)
return err
}
if err = helpers.CopyFile(transferStruct.TorrentFilePath, filepath.Join(transferStruct.Opts.QBitDir, newBaseName+".torrent")); err != nil {
chans.ErrChannel <- fmt.Sprintf("Can't create qBittorrent torrent file %v", filepath.Join(transferStruct.Opts.QBitDir, newBaseName+".torrent"))
return err
}
chans.ComChannel <- fmt.Sprintf("Sucessfully imported %v", key)
return nil
}
func HandleResumeItems(opts *options.Opts, resumeItems map[string]*utorrentStructs.ResumeItem) {
totalJobs := len(resumeItems)
chans := Channels{ComChannel: make(chan string, totalJobs),
ErrChannel: make(chan string, totalJobs),
BoundedChannel: make(chan bool, runtime.GOMAXPROCS(0)*2)}
numJob := 1
var newTags []string
var wg sync.WaitGroup
positionNum := 0
replaces := CreateReplaces(opts.Replaces)
for key, resumeItem := range resumeItems {
positionNum++
if opts.WithoutTags == false {
if resumeItem.Labels != nil {
for _, label := range resumeItem.Labels {
if exists, tag := helpers.CheckExists(helpers.ASCIIConvert(label), newTags); !exists {
newTags = append(newTags, tag)
}
}
}
wg.Add(1)
chans.BoundedChannel <- true
transferStruct := CreateEmptyNewTransferStructure()
transferStruct.ResumeItem = resumeItem
transferStruct.Replace = replaces
transferStruct.Opts = opts
go HandleResumeItem(key, &transferStruct, &chans, &wg)
} else {
totalJobs--
}
}
go func() {
wg.Wait()
close(chans.ComChannel)
close(chans.ErrChannel)
}()
for message := range chans.ComChannel {
fmt.Printf("%v/%v %v \n", numJob, totalJobs, message)
numJob++
}
var wasErrors bool
for message := range chans.ErrChannel {
fmt.Printf("%v/%v %v \n", numJob, totalJobs, message)
wasErrors = true
numJob++
}
if opts.WithoutTags == false {
ProcessLabels(opts, newTags)
}
fmt.Println()
log.Println("Ended")
if wasErrors {
log.Println("Not all torrents was processed")
}
}
// HandleTorrentFilePath check if resume key is absolute path. It means that we should search torrent file using this absolute path
// notice that torrent file name always known
func HandleTorrentFilePath(transferStructure *TransferStructure, key string) {
if fileHelpers.IsAbs(key) {
transferStructure.TorrentFilePath = fileHelpers.Normalize(key, `/`)
transferStructure.TorrentFileName = fileHelpers.Base(key)
} else {
transferStructure.TorrentFilePath = fileHelpers.Join([]string{transferStructure.Opts.BitDir, key}, `/`) // additional search required
transferStructure.TorrentFileName = key
}
}
// if we can find torrent file, we start check another locations from options search paths
func FindTorrentFile(transferStructure *TransferStructure) error {
if _, err := os.Stat(transferStructure.TorrentFilePath); os.IsNotExist(err) {
for _, searchPath := range transferStructure.Opts.SearchPaths {
// normalize path with os.PathSeparator, because file can be on share, for example
fullPath := fileHelpers.Join([]string{searchPath, transferStructure.TorrentFileName}, string(os.PathSeparator))
if _, err = os.Stat(fullPath); err == nil {
transferStructure.TorrentFilePath = fullPath
return nil
}
}
// return error only if we didn't find anything
return fmt.Errorf("can't locate torrent file %v", transferStructure.TorrentFileName)
}
return nil
}