mirror of
https://github.com/rumanzo/bt2qbt.git
synced 2024-11-14 14:44:19 +01:00
cca7869d2a
* 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>
167 lines
5.3 KiB
Go
167 lines
5.3 KiB
Go
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
|
|
}
|