mirror of
https://github.com/rumanzo/bt2qbt.git
synced 2024-11-22 02:12:39 +01:00
Fix windows prohibited simbols (#46)
* handle windows fs prohibited symbols * fix abs path symbols replace * more readable code * better replacer new replacer function with tests * return zero files from torrent info file tree if there are single file torrent * don't harm torrent file function, handle filelist size in another places * more accurate handling single files * use mapped files for directories or torrent names with space on ending * normalize backslases too * Don't normalize cesu8\prohibited symbols on torrent name if torrent file contain several files. But normalize for single file torrent and normalize last space character for both * Better torrent name and torrent file paths handling helpers tests * move torrents functions to torrents functions cache single flag * update README.md * fix deprecation warnings
This commit is contained in:
parent
a25c2670ff
commit
0b305dd2f9
@ -1,3 +1,5 @@
|
|||||||
|
![GitHub all releases](https://img.shields.io/github/downloads/rumanzo/bt2qbt/total)
|
||||||
|
|
||||||
# bt2qbt
|
# bt2qbt
|
||||||
|
|
||||||
bt2qbt is cli tool for export from uTorrent\Bittorrent into qBittorrent (convert)
|
bt2qbt is cli tool for export from uTorrent\Bittorrent into qBittorrent (convert)
|
||||||
@ -5,7 +7,7 @@ bt2qbt is cli tool for export from uTorrent\Bittorrent into qBittorrent (convert
|
|||||||
> Actual version tested with uTorrent 3.5.5 (build 46206) and qBittorrent 4.4.2. It should work with older version utorrent and newer version of qBittorrent, but it isn't tested.
|
> Actual version tested with uTorrent 3.5.5 (build 46206) and qBittorrent 4.4.2. It should work with older version utorrent and newer version of qBittorrent, but it isn't tested.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> In most cases just enough run app. For windows users double click on downloaded exe file. But read notices and warnings below
|
> In most cases just enough run app. For Windows users double-click on downloaded exe file. But read notices and warnings below
|
||||||
>
|
>
|
||||||
- [bt2qbt](#bt2qbt)
|
- [bt2qbt](#bt2qbt)
|
||||||
- [Feature](#user-content-feature)
|
- [Feature](#user-content-feature)
|
||||||
|
@ -15,11 +15,6 @@ func (transfer *TransferStructure) HandleStructures() {
|
|||||||
|
|
||||||
// if torrent name was renamed, add modified name
|
// if torrent name was renamed, add modified name
|
||||||
transfer.HandleCaption()
|
transfer.HandleCaption()
|
||||||
if transfer.TorrentFile.Info.NameUTF8 != "" {
|
|
||||||
transfer.Fastresume.Name = helpers.HandleCesu8(transfer.TorrentFile.Info.NameUTF8)
|
|
||||||
} else {
|
|
||||||
transfer.Fastresume.Name = helpers.HandleCesu8(transfer.TorrentFile.Info.Name)
|
|
||||||
}
|
|
||||||
transfer.Fastresume.ActiveTime = transfer.ResumeItem.Runtime
|
transfer.Fastresume.ActiveTime = transfer.ResumeItem.Runtime
|
||||||
transfer.Fastresume.AddedTime = transfer.ResumeItem.AddedOn
|
transfer.Fastresume.AddedTime = transfer.ResumeItem.AddedOn
|
||||||
transfer.Fastresume.CompletedTime = transfer.ResumeItem.CompletedOn
|
transfer.Fastresume.CompletedTime = transfer.ResumeItem.CompletedOn
|
||||||
@ -46,6 +41,6 @@ func (transfer *TransferStructure) HandleStructures() {
|
|||||||
transfer.NumPieces = int64(len(transfer.TorrentFile.Info.Pieces)) / 20
|
transfer.NumPieces = int64(len(transfer.TorrentFile.Info.Pieces)) / 20
|
||||||
|
|
||||||
transfer.HandleCompleted() // important handle priorities before handling pieces
|
transfer.HandleCompleted() // important handle priorities before handling pieces
|
||||||
transfer.HandleSavePaths()
|
transfer.HandleSavePaths() // and there we handle torrent name also
|
||||||
transfer.HandlePieces()
|
transfer.HandlePieces()
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rumanzo/bt2qbt/internal/options"
|
"github.com/rumanzo/bt2qbt/internal/options"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,7 +13,7 @@ func ProcessLabels(opts *options.Opts, newtags []string) error {
|
|||||||
|
|
||||||
// check if categories is new file. If it exists it must be unmarshaled. Default categories file contains only {}
|
// check if categories is new file. If it exists it must be unmarshaled. Default categories file contains only {}
|
||||||
var categoriesIsNew bool
|
var categoriesIsNew bool
|
||||||
file, err := os.OpenFile(opts.Categories, os.O_RDWR, 0644)
|
_, err := os.Stat(opts.Categories)
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
categoriesIsNew = true
|
categoriesIsNew = true
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@ -22,16 +21,11 @@ func ProcessLabels(opts *options.Opts, newtags []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !categoriesIsNew {
|
if !categoriesIsNew {
|
||||||
dataRaw, err := ioutil.ReadAll(file)
|
dataRaw, err := os.ReadFile(opts.Categories)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("Unexpected error while read categories.json. Error:\n%v\n", err))
|
return errors.New(fmt.Sprintf("Unexpected error while read categories.json. Error:\n%v\n", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = file.Close()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New(fmt.Sprintf("Can't close categories.json. Error:\n%v\n", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(dataRaw, &categories)
|
err = json.Unmarshal(dataRaw, &categories)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("Unexpected error while unmarshaling categories.json. Error:\n%v\n", err))
|
return errors.New(fmt.Sprintf("Unexpected error while unmarshaling categories.json. Error:\n%v\n", err))
|
||||||
@ -56,7 +50,7 @@ func ProcessLabels(opts *options.Opts, newtags []string) error {
|
|||||||
return errors.New(fmt.Sprintf("Can't marshal categories. Error:\n%v\n", err))
|
return errors.New(fmt.Sprintf("Can't marshal categories. Error:\n%v\n", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(opts.Categories, newCategories, 0644)
|
err = os.WriteFile(opts.Categories, newCategories, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("Can't write categories.json. Error:\n%v\n", err))
|
return errors.New(fmt.Sprintf("Can't write categories.json. Error:\n%v\n", err))
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,12 @@ package transfer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/rumanzo/bt2qbt/internal/options"
|
"github.com/rumanzo/bt2qbt/internal/options"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProcessLabelsExisting(t *testing.T) {
|
func TestProcessLabelsExisting(t *testing.T) {
|
||||||
err := ioutil.WriteFile("../../test/categories_existing.json", []byte("{}"), 0755)
|
err := os.WriteFile("../../test/categories_existing.json", []byte("{}"), 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Can't write empty categories test file. Err: %v", err.Error())
|
t.Fatalf("Can't write empty categories test file. Err: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ func HandleTorrentFilePath(transferStructure *TransferStructure, key string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we can find torrent file, we start check another locations from options search paths
|
// FindTorrentFile if we can find torrent file, we start check another locations from options search paths
|
||||||
func FindTorrentFile(transferStructure *TransferStructure) error {
|
func FindTorrentFile(transferStructure *TransferStructure) error {
|
||||||
if _, err := os.Stat(transferStructure.TorrentFilePath); os.IsNotExist(err) {
|
if _, err := os.Stat(transferStructure.TorrentFilePath); os.IsNotExist(err) {
|
||||||
for _, searchPath := range transferStructure.Opts.SearchPaths {
|
for _, searchPath := range transferStructure.Opts.SearchPaths {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/rumanzo/bt2qbt/internal/replace"
|
"github.com/rumanzo/bt2qbt/internal/replace"
|
||||||
"github.com/rumanzo/bt2qbt/pkg/fileHelpers"
|
"github.com/rumanzo/bt2qbt/pkg/fileHelpers"
|
||||||
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
||||||
|
"github.com/rumanzo/bt2qbt/pkg/normalization"
|
||||||
"github.com/rumanzo/bt2qbt/pkg/qBittorrentStructures"
|
"github.com/rumanzo/bt2qbt/pkg/qBittorrentStructures"
|
||||||
"github.com/rumanzo/bt2qbt/pkg/torrentStructures"
|
"github.com/rumanzo/bt2qbt/pkg/torrentStructures"
|
||||||
"github.com/rumanzo/bt2qbt/pkg/utorrentStructs"
|
"github.com/rumanzo/bt2qbt/pkg/utorrentStructs"
|
||||||
@ -15,7 +16,6 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//goland:noinspection GoNameStartsWithPackageName
|
//goland:noinspection GoNameStartsWithPackageName
|
||||||
@ -82,13 +82,13 @@ func (transfer *TransferStructure) HandleCaption() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HandleState transfer torrents state.
|
// HandleState transfer torrents state.
|
||||||
// if torrent has several files and it doesn't complete downloaded (priority), it will be stopped
|
// if torrent has several files, and it doesn't complete downloaded (priority), it will be stopped
|
||||||
func (transfer *TransferStructure) HandleState() {
|
func (transfer *TransferStructure) HandleState() {
|
||||||
if transfer.ResumeItem.Started == 0 {
|
if transfer.ResumeItem.Started == 0 {
|
||||||
transfer.Fastresume.Paused = 1
|
transfer.Fastresume.Paused = 1
|
||||||
transfer.Fastresume.AutoManaged = 0
|
transfer.Fastresume.AutoManaged = 0
|
||||||
} else {
|
} else {
|
||||||
if len(transfer.TorrentFile.GetFileList()) > 1 {
|
if !transfer.TorrentFile.IsSingle() {
|
||||||
var parted bool
|
var parted bool
|
||||||
for _, prio := range transfer.Fastresume.FilePriority {
|
for _, prio := range transfer.Fastresume.FilePriority {
|
||||||
if prio == 0 {
|
if prio == 0 {
|
||||||
@ -202,7 +202,7 @@ func (transfer *TransferStructure) HandlePieces() {
|
|||||||
if transfer.Fastresume.Unfinished != nil {
|
if transfer.Fastresume.Unfinished != nil {
|
||||||
transfer.FillWholePieces(0)
|
transfer.FillWholePieces(0)
|
||||||
} else {
|
} else {
|
||||||
if len(transfer.TorrentFile.GetFileList()) > 0 {
|
if !transfer.TorrentFile.IsSingle() {
|
||||||
transfer.FillPiecesParted()
|
transfer.FillPiecesParted()
|
||||||
} else {
|
} else {
|
||||||
transfer.FillWholePieces(1)
|
transfer.FillWholePieces(1)
|
||||||
@ -227,7 +227,8 @@ func (transfer *TransferStructure) FillPiecesParted() {
|
|||||||
}
|
}
|
||||||
var fileOffsets []Offset
|
var fileOffsets []Offset
|
||||||
bytesLength := int64(0)
|
bytesLength := int64(0)
|
||||||
for _, file := range transfer.TorrentFile.GetFileListWB() { // need to adapt for torrents v2 version
|
fileList, _ := transfer.TorrentFile.GetFileListWB()
|
||||||
|
for _, file := range fileList {
|
||||||
fileFirstOffset := bytesLength + 1
|
fileFirstOffset := bytesLength + 1
|
||||||
bytesLength += file.Length
|
bytesLength += file.Length
|
||||||
fileLastOffset := bytesLength
|
fileLastOffset := bytesLength
|
||||||
@ -267,31 +268,20 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
|||||||
// Original paths always ending with pathSeparator
|
// Original paths always ending with pathSeparator
|
||||||
// SubFolder or NoSubfolder never have ending pathSeparator
|
// SubFolder or NoSubfolder never have ending pathSeparator
|
||||||
// qBtSavePath always has separator /, otherwise SavePath use os pathSeparator
|
// qBtSavePath always has separator /, otherwise SavePath use os pathSeparator
|
||||||
|
|
||||||
if transfer.Magnet {
|
if transfer.Magnet {
|
||||||
transfer.Fastresume.QBtContentLayout = "Original"
|
transfer.Fastresume.QBtContentLayout = "Original"
|
||||||
transfer.Fastresume.QbtSavePath = fileHelpers.Normalize(helpers.HandleCesu8(transfer.ResumeItem.Path), "/")
|
transfer.Fastresume.QbtSavePath = fileHelpers.Normalize(helpers.HandleCesu8(transfer.ResumeItem.Path), "/")
|
||||||
} else {
|
} else {
|
||||||
var torrentName string
|
var nameNormalized bool
|
||||||
var torrentNameOriginal string
|
transfer.Fastresume.Name, nameNormalized = normalization.FullNormalize(transfer.TorrentFile.GetTorrentName())
|
||||||
if transfer.TorrentFile.Info.NameUTF8 != "" {
|
|
||||||
torrentName = helpers.HandleCesu8(transfer.TorrentFile.Info.NameUTF8)
|
|
||||||
torrentNameOriginal = transfer.TorrentFile.Info.NameUTF8
|
|
||||||
} else {
|
|
||||||
torrentName = helpers.HandleCesu8(transfer.TorrentFile.Info.Name)
|
|
||||||
torrentNameOriginal = transfer.TorrentFile.Info.Name
|
|
||||||
}
|
|
||||||
lastPathName := fileHelpers.Base(helpers.HandleCesu8(transfer.ResumeItem.Path))
|
|
||||||
|
|
||||||
if len(transfer.TorrentFile.GetFileList()) > 0 {
|
lastPathName := fileHelpers.Base(helpers.HandleCesu8(transfer.ResumeItem.Path))
|
||||||
var cesu8FilesExists bool
|
// if FileList contain only 1 file that means it is single file torrent
|
||||||
for _, filePath := range transfer.TorrentFile.GetFileList() {
|
if !transfer.TorrentFile.IsSingle() {
|
||||||
cesuEncodedFilepath := helpers.HandleCesu8(filePath)
|
fileList, filesNormalized := transfer.TorrentFile.GetFileList()
|
||||||
if utf8.RuneCountInString(filePath) != utf8.RuneCountInString(cesuEncodedFilepath) {
|
|
||||||
cesu8FilesExists = true
|
if lastPathName == transfer.Fastresume.Name && !filesNormalized && !nameNormalized {
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lastPathName == torrentName && !cesu8FilesExists {
|
|
||||||
transfer.Fastresume.QBtContentLayout = "Original"
|
transfer.Fastresume.QBtContentLayout = "Original"
|
||||||
transfer.Fastresume.QbtSavePath = fileHelpers.CutLastPath(helpers.HandleCesu8(transfer.ResumeItem.Path), transfer.Opts.PathSeparator)
|
transfer.Fastresume.QbtSavePath = fileHelpers.CutLastPath(helpers.HandleCesu8(transfer.ResumeItem.Path), transfer.Opts.PathSeparator)
|
||||||
if maxIndex := transfer.FindHighestIndexOfMappedFiles(); maxIndex >= 0 {
|
if maxIndex := transfer.FindHighestIndexOfMappedFiles(); maxIndex >= 0 {
|
||||||
@ -309,7 +299,7 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
|||||||
pathParts[num] = helpers.HandleCesu8(part.(string))
|
pathParts[num] = helpers.HandleCesu8(part.(string))
|
||||||
}
|
}
|
||||||
// we have to append torrent name(from torrent file) at the top of path
|
// we have to append torrent name(from torrent file) at the top of path
|
||||||
transfer.Fastresume.MappedFiles[index] = fileHelpers.Join(append([]string{torrentName}, pathParts...), transfer.Opts.PathSeparator)
|
transfer.Fastresume.MappedFiles[index] = fileHelpers.Join(append([]string{transfer.Fastresume.Name}, pathParts...), transfer.Opts.PathSeparator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,10 +309,10 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
transfer.Fastresume.QBtContentLayout = "NoSubfolder"
|
transfer.Fastresume.QBtContentLayout = "NoSubfolder"
|
||||||
// NoSubfolder always has full mapped files
|
// NoSubfolder always has full mapped files, so we append all of them
|
||||||
// so we append all of them
|
for _, filePath := range fileList {
|
||||||
for _, filePath := range transfer.TorrentFile.GetFileList() {
|
transfer.Fastresume.MappedFiles = append(transfer.Fastresume.MappedFiles,
|
||||||
transfer.Fastresume.MappedFiles = append(transfer.Fastresume.MappedFiles, fileHelpers.Normalize(helpers.HandleCesu8(filePath), transfer.Opts.PathSeparator))
|
fileHelpers.Normalize(filePath, transfer.Opts.PathSeparator))
|
||||||
}
|
}
|
||||||
// and then doing remap if resumeItem contain target field
|
// and then doing remap if resumeItem contain target field
|
||||||
if maxIndex := transfer.FindHighestIndexOfMappedFiles(); maxIndex >= 0 {
|
if maxIndex := transfer.FindHighestIndexOfMappedFiles(); maxIndex >= 0 {
|
||||||
@ -344,7 +334,7 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
transfer.Fastresume.QBtContentLayout = "Original" // utorrent\bittorrent don't support create subfolders for torrents with single file
|
transfer.Fastresume.QBtContentLayout = "Original" // utorrent\bittorrent don't support create subfolders for torrents with single file
|
||||||
if lastPathName != torrentNameOriginal {
|
if nameNormalized || lastPathName != transfer.Fastresume.Name {
|
||||||
//it means that we have renamed path and targets item, and should have mapped files
|
//it means that we have renamed path and targets item, and should have mapped files
|
||||||
transfer.Fastresume.MappedFiles = []string{lastPathName}
|
transfer.Fastresume.MappedFiles = []string{lastPathName}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent.txt`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -59,6 +60,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `E:/newfolder/`,
|
QbtSavePath: `E:/newfolder/`,
|
||||||
SavePath: `E:\newfolder\`,
|
SavePath: `E:\newfolder\`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent.txt`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -79,6 +81,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent.txt`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -107,6 +110,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent.txt`,
|
||||||
MappedFiles: []string{`renamed_test_torrent.txt`},
|
MappedFiles: []string{`renamed_test_torrent.txt`},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -135,6 +139,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `/mnt/d/torrents/`,
|
QbtSavePath: `/mnt/d/torrents/`,
|
||||||
SavePath: `/mnt/d/torrents/`,
|
SavePath: `/mnt/d/torrents/`,
|
||||||
|
Name: `test_torrent.txt`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
MappedFiles: []string{`renamed_test_torrent.txt`},
|
MappedFiles: []string{`renamed_test_torrent.txt`},
|
||||||
},
|
},
|
||||||
@ -162,6 +167,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -187,6 +193,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `E:/newfolder/`,
|
QbtSavePath: `E:/newfolder/`,
|
||||||
SavePath: `E:\newfolder\`,
|
SavePath: `E:\newfolder\`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -212,6 +219,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -245,6 +253,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent`,
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
``,
|
``,
|
||||||
``,
|
``,
|
||||||
@ -256,7 +265,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "010 Test torrent with windows folder (original) path with replace to linux paths and linux sep. Renamed File",
|
name: "010 Test torrent with windows folder (original) path with replace to linux paths and linux sep. Renamed File",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `D:\torrents\test_torrent`,
|
Path: `D:\torrents\test_torrent`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -283,6 +292,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `/mnt/d/torrents/`,
|
QbtSavePath: `/mnt/d/torrents/`,
|
||||||
SavePath: `/mnt/d/torrents/`,
|
SavePath: `/mnt/d/torrents/`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
|
Name: `test_torrent`,
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
``,
|
``,
|
||||||
``,
|
``,
|
||||||
@ -294,7 +304,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "011 Test torrent with windows folder (NoSubfolder) path without replaces",
|
name: "011 Test torrent with windows folder (NoSubfolder) path without replaces",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
||||||
TorrentFile: &torrentStructures.Torrent{
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
Info: &torrentStructures.TorrentInfo{
|
Info: &torrentStructures.TorrentInfo{
|
||||||
@ -312,6 +322,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/test`,
|
QbtSavePath: `D:/torrents/test`,
|
||||||
SavePath: `D:\torrents\test`,
|
SavePath: `D:\torrents\test`,
|
||||||
|
Name: `test_torrent`,
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1\file1.txt`,
|
`dir1\file1.txt`,
|
||||||
`dir2\file2.txt`,
|
`dir2\file2.txt`,
|
||||||
@ -324,7 +335,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "012 Test torrent with windows folder (NoSubfolder) path with replace",
|
name: "012 Test torrent with windows folder (NoSubfolder) path with replace",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
||||||
TorrentFile: &torrentStructures.Torrent{
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
Info: &torrentStructures.TorrentInfo{
|
Info: &torrentStructures.TorrentInfo{
|
||||||
@ -342,6 +353,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `E:/newfolder/test`,
|
QbtSavePath: `E:/newfolder/test`,
|
||||||
SavePath: `E:\newfolder\test`,
|
SavePath: `E:\newfolder\test`,
|
||||||
|
Name: `test_torrent`,
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1\file1.txt`,
|
`dir1\file1.txt`,
|
||||||
`dir2\file2.txt`,
|
`dir2\file2.txt`,
|
||||||
@ -354,7 +366,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "013 Test torrent with windows folder (NoSubfolder) path without replaces. NameUTF8",
|
name: "013 Test torrent with windows folder (NoSubfolder) path without replaces. NameUTF8",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
||||||
TorrentFile: &torrentStructures.Torrent{
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
Info: &torrentStructures.TorrentInfo{
|
Info: &torrentStructures.TorrentInfo{
|
||||||
@ -372,6 +384,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/test`,
|
QbtSavePath: `D:/torrents/test`,
|
||||||
SavePath: `D:\torrents\test`,
|
SavePath: `D:\torrents\test`,
|
||||||
|
Name: `test_torrent`,
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1\file1.txt`,
|
`dir1\file1.txt`,
|
||||||
`dir2\file2.txt`,
|
`dir2\file2.txt`,
|
||||||
@ -384,7 +397,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "014 Test torrent with windows folder (NoSubfolder) path without replaces. Renamed File",
|
name: "014 Test torrent with windows folder (NoSubfolder) path without replaces. Renamed File",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `D:\torrents\test`,
|
Path: `D:\torrents\test`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -411,6 +424,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
QbtSavePath: `D:/torrents/test`,
|
QbtSavePath: `D:/torrents/test`,
|
||||||
SavePath: `D:\torrents\test`,
|
SavePath: `D:\torrents\test`,
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
Name: `test_torrent`,
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1\file1.txt`,
|
`dir1\file1.txt`,
|
||||||
`dir2\file2.txt`,
|
`dir2\file2.txt`,
|
||||||
@ -422,7 +436,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "015 Test torrent with windows folder (NoSubfolder) path with replace to linux paths and linux sep. Renamed File",
|
name: "015 Test torrent with windows folder (NoSubfolder) path with replace to linux paths and linux sep. Renamed File",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `D:\torrents\test`,
|
Path: `D:\torrents\test`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -448,6 +462,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `/mnt/d/torrents/test`,
|
QbtSavePath: `/mnt/d/torrents/test`,
|
||||||
SavePath: `/mnt/d/torrents/test`,
|
SavePath: `/mnt/d/torrents/test`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1/file1.txt`,
|
`dir1/file1.txt`,
|
||||||
@ -478,6 +493,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/test`,
|
QbtSavePath: `D:/torrents/test`,
|
||||||
SavePath: `D:\torrents\test`,
|
SavePath: `D:\torrents\test`,
|
||||||
|
Name: `test_torrent`,
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1\file1.txt`,
|
`dir1\file1.txt`,
|
||||||
`dir2\file2.txt`,
|
`dir2\file2.txt`,
|
||||||
@ -491,7 +507,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
name: "017 Test torrent with windows single nofolder (original) path without replaces",
|
name: "017 Test torrent with windows single nofolder (original) path without replaces",
|
||||||
mustFail: true,
|
mustFail: true,
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent.txt`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test_torrent.txt`},
|
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test_torrent.txt`},
|
||||||
TorrentFile: &torrentStructures.Torrent{
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
Info: &torrentStructures.TorrentInfo{
|
Info: &torrentStructures.TorrentInfo{
|
||||||
@ -504,6 +520,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torre`,
|
SavePath: `D:\torre`,
|
||||||
|
Name: `test_torrent.txt`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -511,7 +528,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "018 Test torrent with windows folder (original) path without replaces. Moved files with absolute paths",
|
name: "018 Test torrent with windows folder (original) path without replaces. Moved files with absolute paths",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `D:\torrents\test_torrent`,
|
Path: `D:\torrents\test_torrent`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -547,6 +564,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
``,
|
``,
|
||||||
@ -561,7 +579,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "019 Test torrent with windows folder (original) path with replaces. Moved files with absolute paths",
|
name: "019 Test torrent with windows folder (original) path with replaces. Moved files with absolute paths",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `D:\torrents\test_torrent`,
|
Path: `D:\torrents\test_torrent`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -597,6 +615,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `/mnt/d/torrents/`,
|
QbtSavePath: `/mnt/d/torrents/`,
|
||||||
SavePath: `/mnt/d/torrents/`,
|
SavePath: `/mnt/d/torrents/`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
``,
|
``,
|
||||||
@ -611,7 +630,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "020 Test torrent with windows folder (NoSubfolder) path without replaces. Moved files with absolute paths",
|
name: "020 Test torrent with windows folder (NoSubfolder) path without replaces. Moved files with absolute paths",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `D:\torrents\test`,
|
Path: `D:\torrents\test`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -647,6 +666,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/test`,
|
QbtSavePath: `D:/torrents/test`,
|
||||||
SavePath: `D:\torrents\test`,
|
SavePath: `D:\torrents\test`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1\file1.txt`,
|
`dir1\file1.txt`,
|
||||||
@ -661,7 +681,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "021 Test torrent with windows share folder (Original) path without replaces. Moved files with absolute paths. Windows share",
|
name: "021 Test torrent with windows share folder (Original) path without replaces. Moved files with absolute paths. Windows share",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `\\torrents\test_torrent`,
|
Path: `\\torrents\test_torrent`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -697,6 +717,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `//torrents/`,
|
QbtSavePath: `//torrents/`,
|
||||||
SavePath: `\\torrents\`,
|
SavePath: `\\torrents\`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
``,
|
``,
|
||||||
@ -711,7 +732,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "022 Test torrent with windows share folder (NoSubfolder) path without replaces. Moved files with absolute paths",
|
name: "022 Test torrent with windows share folder (NoSubfolder) path without replaces. Moved files with absolute paths",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `\\torrents\test`,
|
Path: `\\torrents\test`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -747,6 +768,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `//torrents/test`,
|
QbtSavePath: `//torrents/test`,
|
||||||
SavePath: `\\torrents\test`,
|
SavePath: `\\torrents\test`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1\file1.txt`,
|
`dir1\file1.txt`,
|
||||||
@ -761,7 +783,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "023 Test torrent with windows folder (Original) path without replaces. Absolute paths. Windows share Replace",
|
name: "023 Test torrent with windows folder (Original) path without replaces. Absolute paths. Windows share Replace",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
Path: `\\torrents\test_torrent`,
|
Path: `\\torrents\test_torrent`,
|
||||||
Targets: [][]interface{}{
|
Targets: [][]interface{}{
|
||||||
@ -797,6 +819,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `//torrents/`,
|
QbtSavePath: `//torrents/`,
|
||||||
SavePath: `//torrents/`,
|
SavePath: `//torrents/`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
``,
|
``,
|
||||||
@ -847,6 +870,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `//torrents/test`,
|
QbtSavePath: `//torrents/test`,
|
||||||
SavePath: `//torrents/test`,
|
SavePath: `//torrents/test`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`dir1/file1.txt`,
|
`dir1/file1.txt`,
|
||||||
@ -866,7 +890,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Path: `D:\torrents\test`,
|
Path: `D:\torrents\test`,
|
||||||
},
|
},
|
||||||
TorrentFile: &torrentStructures.Torrent{
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
Info: &torrentStructures.TorrentInfo{},
|
Info: &torrentStructures.TorrentInfo{Name: "torrentname"},
|
||||||
},
|
},
|
||||||
Opts: &options.Opts{PathSeparator: `\`},
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
Magnet: true,
|
Magnet: true,
|
||||||
@ -875,6 +899,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/test`,
|
QbtSavePath: `D:/torrents/test`,
|
||||||
SavePath: `D:\torrents\test`,
|
SavePath: `D:\torrents\test`,
|
||||||
|
Name: "torrentname",
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -897,6 +922,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/`,
|
QbtSavePath: `D:/`,
|
||||||
SavePath: `D:\`,
|
SavePath: `D:\`,
|
||||||
|
Name: `test.txt`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -926,6 +952,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/`,
|
QbtSavePath: `D:/`,
|
||||||
SavePath: `D:\`,
|
SavePath: `D:\`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -967,6 +994,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
|
Name: "test_torrent \xf0\x9f\x86\x95",
|
||||||
QBtContentLayout: "Original",
|
QBtContentLayout: "Original",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
||||||
@ -997,6 +1025,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: "D:/torrents/renamed test_torrent \xf0\x9f\x86\x95",
|
QbtSavePath: "D:/torrents/renamed test_torrent \xf0\x9f\x86\x95",
|
||||||
SavePath: "D:\\torrents\\renamed test_torrent \xf0\x9f\x86\x95",
|
SavePath: "D:\\torrents\\renamed test_torrent \xf0\x9f\x86\x95",
|
||||||
|
Name: "test_torrent \xf0\x9f\x86\x95",
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||||
@ -1043,6 +1072,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: "D:/torrents/test_torrent \xf0\x9f\x86\x95",
|
QbtSavePath: "D:/torrents/test_torrent \xf0\x9f\x86\x95",
|
||||||
SavePath: "D:\\torrents\\test_torrent \xf0\x9f\x86\x95",
|
SavePath: "D:\\torrents\\test_torrent \xf0\x9f\x86\x95",
|
||||||
|
Name: "test_torrent \xf0\x9f\x86\x95",
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
||||||
@ -1073,6 +1103,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: "D:/torrents/renamed test_torrent \xf0\x9f\x86\x95",
|
QbtSavePath: "D:/torrents/renamed test_torrent \xf0\x9f\x86\x95",
|
||||||
SavePath: "D:\\torrents\\renamed test_torrent \xf0\x9f\x86\x95",
|
SavePath: "D:\\torrents\\renamed test_torrent \xf0\x9f\x86\x95",
|
||||||
|
Name: "test_torrent \xf0\x9f\x86\x95",
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||||
@ -1105,6 +1136,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: "D:/torrents/test_torrent",
|
QbtSavePath: "D:/torrents/test_torrent",
|
||||||
SavePath: "D:\\torrents\\test_torrent",
|
SavePath: "D:\\torrents\\test_torrent",
|
||||||
|
Name: "test_torrent",
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||||
@ -1156,6 +1188,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: "D:/torrents/test_torrent",
|
QbtSavePath: "D:/torrents/test_torrent",
|
||||||
SavePath: "D:\\torrents\\test_torrent",
|
SavePath: "D:\\torrents\\test_torrent",
|
||||||
|
Name: "test_torrent",
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
||||||
@ -1184,6 +1217,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/`,
|
QbtSavePath: `D:/torrents/`,
|
||||||
SavePath: `D:\torrents\`,
|
SavePath: `D:\torrents\`,
|
||||||
|
Name: "test_torrent \xf0\x9f\x86\x95.txt",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
"test_torrent \xf0\x9f\x86\x95.txt",
|
"test_torrent \xf0\x9f\x86\x95.txt",
|
||||||
},
|
},
|
||||||
@ -1211,6 +1245,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
QbtSavePath: `D:/torrents/renamed test_torrent`,
|
QbtSavePath: `D:/torrents/renamed test_torrent`,
|
||||||
SavePath: `D:\torrents\renamed test_torrent`,
|
SavePath: `D:\torrents\renamed test_torrent`,
|
||||||
|
Name: `test_torrent`,
|
||||||
QBtContentLayout: "NoSubfolder",
|
QBtContentLayout: "NoSubfolder",
|
||||||
MappedFiles: []string{
|
MappedFiles: []string{
|
||||||
`#test _ test [01]{1} [6K].jpg`,
|
`#test _ test [01]{1} [6K].jpg`,
|
||||||
@ -1220,6 +1255,435 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
Opts: &options.Opts{PathSeparator: `\`},
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "035 Test torrent with windows folder (NoSubfolder) with prohibited symbols.",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\renamed test_torrent`},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{`#test | test [01]{1} [6K].jpg`}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{`testdir1 collection`, `testdir2?`, `1.jpg`}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/torrents/renamed test_torrent`,
|
||||||
|
SavePath: `D:\torrents\renamed test_torrent`,
|
||||||
|
Name: `test_torrent`,
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`#test _ test [01]{1} [6K].jpg`,
|
||||||
|
`testdir1 collection\testdir2_\1.jpg`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "036 Test torrent with windows single nofolder (original) path without replaces. With prohibited symbols",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test_torrent.txt`},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: `test|torrent.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
Name: `test_torrent.txt`,
|
||||||
|
QbtSavePath: `D:/torrents/`,
|
||||||
|
SavePath: `D:\torrents\`,
|
||||||
|
QBtContentLayout: "Original",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`test_torrent.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "037 Test torrent with windows folder (NoSubfolder) with prohibited symbols. *nix path separator",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\renamed test_torrent`},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{`#test | test [01]{1} [6K].jpg`}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{`testdir1 collection`, `testdir2?`, `1.jpg`}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `/`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/torrents/renamed test_torrent`,
|
||||||
|
SavePath: `D:/torrents/renamed test_torrent`,
|
||||||
|
Name: `test_torrent`,
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`#test _ test [01]{1} [6K].jpg`,
|
||||||
|
`testdir1 collection/testdir2_/1.jpg`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "038 Test torrent with windows single nofolder (original) path without replaces. With prohibited symbols. *nix path separator",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test_torrent.txt`},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: `test|torrent.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `/`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/torrents/`,
|
||||||
|
SavePath: `D:/torrents/`,
|
||||||
|
Name: `test_torrent.txt`,
|
||||||
|
QBtContentLayout: "Original",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`test_torrent.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "039 Test torrent with windows single nofolder (original) path with renamed file. v2 torrent",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: `D:\torrents\testtorrent.txt`,
|
||||||
|
Targets: [][]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
int64(0),
|
||||||
|
"testtorrent.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: `testtorrent.txt`,
|
||||||
|
FileTree: map[string]interface{}{
|
||||||
|
`testtorrent.txt`: map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(100),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/torrents/`,
|
||||||
|
SavePath: `D:\torrents\`,
|
||||||
|
Name: `testtorrent.txt`,
|
||||||
|
QBtContentLayout: "Original",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "040 Test torrent with windows folder (original) path with renamed file. v2 torrent",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: `D:\torrents\testtorrent`,
|
||||||
|
Targets: [][]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
int64(1),
|
||||||
|
"testtorrent2.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: `testtorrent`,
|
||||||
|
FileTree: map[string]interface{}{
|
||||||
|
`testtorrent1.txt`: map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(100),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`testtorrent3.txt`: map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(100),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/torrents/`,
|
||||||
|
SavePath: `D:\torrents\`,
|
||||||
|
Name: `testtorrent`,
|
||||||
|
QBtContentLayout: "Original",
|
||||||
|
MappedFiles: []string{
|
||||||
|
``,
|
||||||
|
`testtorrent\testtorrent2.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "041 Test torrent with multi file torrent (NoSubfolder) with prohibited symbols in name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: `D:\test_torrent_test`,
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent/test",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir2", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file2.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/test_torrent_test`,
|
||||||
|
SavePath: `D:\test_torrent_test`,
|
||||||
|
Name: `test_torrent_test`,
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`dir1\file1.txt`,
|
||||||
|
`dir2\file2.txt`,
|
||||||
|
`file0.txt`,
|
||||||
|
`file1.txt`,
|
||||||
|
`file2.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "042 Test torrent with multi file torrent with renames (NoSubFolder) with prohibited symbols in name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: `D:\test_torrent_test`,
|
||||||
|
Targets: [][]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
int64(2),
|
||||||
|
"file0_file.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent/test",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir2", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file2.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/test_torrent_test`,
|
||||||
|
SavePath: `D:\test_torrent_test`,
|
||||||
|
Name: `test_torrent_test`,
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`dir1\file1.txt`,
|
||||||
|
`dir2\file2.txt`,
|
||||||
|
`file0_file.txt`,
|
||||||
|
`file1.txt`,
|
||||||
|
`file2.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "043 Test torrent with multi file torrent (NoSubfolder) with space at the end of torrent name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: `D:\test_torrent_`,
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent ",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir2", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file2.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/test_torrent_`,
|
||||||
|
SavePath: `D:\test_torrent_`,
|
||||||
|
Name: `test_torrent_`,
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`dir1\file1.txt`,
|
||||||
|
`dir2\file2.txt`,
|
||||||
|
`file0.txt`,
|
||||||
|
`file1.txt`,
|
||||||
|
`file2.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "044 Test torrent with multi file torrent (NoSubfolder) with space at the end directory",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: `D:\test_torrent_`,
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent ",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1 ", "dir2 ", "file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir3 ", "file2.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/test_torrent_`,
|
||||||
|
SavePath: `D:\test_torrent_`,
|
||||||
|
Name: `test_torrent_`,
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`file0.txt`,
|
||||||
|
`file1.txt`,
|
||||||
|
`file2.txt`,
|
||||||
|
`dir1_\dir2_\file1.txt`,
|
||||||
|
`dir3_\file2.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "045 Test torrent with multi file torrent (NoSubfolder) with space at the end directory with renamed files",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: `D:\test_torrent_`,
|
||||||
|
Targets: [][]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
int64(2),
|
||||||
|
"file2_renamed.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent ",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1 ", "dir2 ", "file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir3 ", "file2.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/test_torrent_`,
|
||||||
|
SavePath: `D:\test_torrent_`,
|
||||||
|
Name: `test_torrent_`,
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
`file0.txt`,
|
||||||
|
`file1.txt`,
|
||||||
|
`file2_renamed.txt`,
|
||||||
|
`dir1_\dir2_\file1.txt`,
|
||||||
|
`dir3_\file2.txt`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "046 Test torrent with multi file torrent with transfer to NoSubfolder cesu8 symbols in names",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: "D:\\test_slashes_emoji \xed\xa0\xbc\xed\xb6\x95_",
|
||||||
|
Caption: "test_slashes_emoji \xed\xa0\xbc\xed\xb6\x95_",
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_slashes/emoji \xed\xa0\xbc\xed\xb6\x95 ",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file_with/slash.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ", "file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ", "file_with/slash.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"testdir_with_space ", "file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"testdir_with_space ", "file_with/slash.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: "D:/test_slashes_emoji \xf0\x9f\x86\x95_",
|
||||||
|
SavePath: "D:\\test_slashes_emoji \xf0\x9f\x86\x95_",
|
||||||
|
Name: "test_slashes_emoji \xf0\x9f\x86\x95_",
|
||||||
|
QbtName: "test_slashes_emoji \xf0\x9f\x86\x95_",
|
||||||
|
QBtContentLayout: `NoSubfolder`,
|
||||||
|
MappedFiles: []string{
|
||||||
|
"file_with_emoji \xf0\x9f\x86\x95.txt",
|
||||||
|
"file_with_slash.txt",
|
||||||
|
"testdir_with_emoji_and_space \xf0\x9f\x86\x95_\\file_with_emoji \xf0\x9f\x86\x95.txt",
|
||||||
|
"testdir_with_emoji_and_space \xf0\x9f\x86\x95_\\file_with_slash.txt",
|
||||||
|
"testdir_with_space_\\file_with_emoji \xf0\x9f\x86\x95.txt",
|
||||||
|
"testdir_with_space_\\file_with_slash.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
@ -1228,6 +1692,8 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
testCase.newTransferStructure.Replace = replaces
|
testCase.newTransferStructure.Replace = replaces
|
||||||
testCase.expected.Replace = replaces
|
testCase.expected.Replace = replaces
|
||||||
}
|
}
|
||||||
|
testCase.newTransferStructure.Fastresume.Name, _ = testCase.newTransferStructure.TorrentFile.GetNormalizedTorrentName()
|
||||||
|
testCase.newTransferStructure.HandleCaption()
|
||||||
testCase.newTransferStructure.HandleSavePaths()
|
testCase.newTransferStructure.HandleSavePaths()
|
||||||
equal := reflect.DeepEqual(testCase.expected.Fastresume, testCase.newTransferStructure.Fastresume)
|
equal := reflect.DeepEqual(testCase.expected.Fastresume, testCase.newTransferStructure.Fastresume)
|
||||||
if !equal && !testCase.mustFail {
|
if !equal && !testCase.mustFail {
|
||||||
@ -1235,9 +1701,9 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err.Error())
|
t.Error(err.Error())
|
||||||
}
|
}
|
||||||
t.Fatalf("Unexpected error: opts isn't equal:\n Got: %#v \n Expect %#v \n Diff: %v\n", testCase.newTransferStructure.Fastresume, testCase.expected.Fastresume, spew.Sdump(changes))
|
t.Fatalf("Unexpected error: opts isn't equal:\nGot: %#v\nExpect %#v\nDiff: %v\n", testCase.newTransferStructure.Fastresume, testCase.expected.Fastresume, spew.Sdump(changes))
|
||||||
} else if equal && testCase.mustFail {
|
} else if equal && testCase.mustFail {
|
||||||
t.Fatalf("Unexpected error: structures are equal, but they shouldn't\n Got: %v\n", spew.Sdump(testCase.newTransferStructure.Fastresume))
|
t.Fatalf("Unexpected error: structures are equal, but they shouldn't\nGot: %v\n", spew.Sdump(testCase.newTransferStructure.Fastresume))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ func TestJoin(t *testing.T) {
|
|||||||
expected: `..\..\testdir\my test file.txt`,
|
expected: `..\..\testdir\my test file.txt`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "009 windows relative paths join. check normalize",
|
name: "009 linux relative paths join. check normalize",
|
||||||
paths: []string{`./testdir/../testdir/../testdir/./`, `my test file.txt`},
|
paths: []string{`./testdir/../testdir/../testdir/./`, `my test file.txt`},
|
||||||
separator: `/`,
|
separator: `/`,
|
||||||
expected: `testdir/my test file.txt`,
|
expected: `testdir/my test file.txt`,
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
"github.com/crazytyper/go-cesu8"
|
"github.com/crazytyper/go-cesu8"
|
||||||
"github.com/zeebo/bencode"
|
"github.com/zeebo/bencode"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -35,7 +35,7 @@ func CheckExists(s string, arr []string) (bool, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DecodeTorrentFile(path string, decodeTo interface{}) error {
|
func DecodeTorrentFile(path string, decodeTo interface{}) error {
|
||||||
dat, err := ioutil.ReadFile(path)
|
dat, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -119,3 +119,9 @@ func HandleCesu8(str string) string {
|
|||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReplaceAllSymbols Replace all symbols in set to replacer
|
||||||
|
func ReplaceAllSymbols(str string, set string, replacer string) string {
|
||||||
|
re := regexp.MustCompilePOSIX(`[` + regexp.QuoteMeta(set) + `]`)
|
||||||
|
return re.ReplaceAllString(str, replacer)
|
||||||
|
}
|
||||||
|
@ -57,6 +57,7 @@ func TestDecodeTorrentFile(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmojiCesu8(t *testing.T) {
|
func TestEmojiCesu8(t *testing.T) {
|
||||||
cesu8 := "normal_text \xed\xa0\xbc\xed\xb6\x95 normal_text \xed\xa0\xbd\xed\xba\x9c.txt.torrent"
|
cesu8 := "normal_text \xed\xa0\xbc\xed\xb6\x95 normal_text \xed\xa0\xbd\xed\xba\x9c.txt.torrent"
|
||||||
utf8 := "normal_text \xf0\x9f\x86\x95 normal_text \xf0\x9f\x9a\x9c.txt.torrent"
|
utf8 := "normal_text \xf0\x9f\x86\x95 normal_text \xf0\x9f\x9a\x9c.txt.torrent"
|
||||||
@ -64,3 +65,71 @@ func TestEmojiCesu8(t *testing.T) {
|
|||||||
t.Fatalf("Cesu8 to utf-8 transformation fail")
|
t.Fatalf("Cesu8 to utf-8 transformation fail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func TestReplaceAllSymbols(t *testing.T) {
|
||||||
|
type Case struct {
|
||||||
|
name string
|
||||||
|
str string
|
||||||
|
set string
|
||||||
|
replacer string
|
||||||
|
expected string
|
||||||
|
}
|
||||||
|
cases := []Case{
|
||||||
|
{
|
||||||
|
name: "001 one symbol",
|
||||||
|
str: `qwerty`,
|
||||||
|
set: `qry`,
|
||||||
|
replacer: `_`,
|
||||||
|
expected: `_we_t_`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "002 several replacer symbol",
|
||||||
|
str: `qwerty`,
|
||||||
|
set: `qry`,
|
||||||
|
replacer: `AAA`,
|
||||||
|
expected: `AAAweAAAtAAA`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "003 several replacer symbol that exists in str",
|
||||||
|
str: `qwerty`,
|
||||||
|
set: `qry`,
|
||||||
|
replacer: `qwerty`,
|
||||||
|
expected: `qwertyweqwertytqwerty`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "004 several replacer symbol that exists in str with special symbols",
|
||||||
|
str: `[qwerty]`,
|
||||||
|
set: `[qry]`,
|
||||||
|
replacer: `qwerty`,
|
||||||
|
expected: `qwertyqwertyweqwertytqwertyqwerty`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "005 several replacer symbol that exists in str with special symbols",
|
||||||
|
str: `[qwerty]`,
|
||||||
|
set: `[qry]`,
|
||||||
|
replacer: `[qwerty]`,
|
||||||
|
expected: `[qwerty][qwerty]we[qwerty]t[qwerty][qwerty]`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "006 emoji replace",
|
||||||
|
str: `qwer🚎y`,
|
||||||
|
set: `🚎`,
|
||||||
|
replacer: `_`,
|
||||||
|
expected: `qwer_y`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "006 two emoji replace",
|
||||||
|
str: `qwer🚎y👍`,
|
||||||
|
set: `🚎😊`,
|
||||||
|
replacer: `_`,
|
||||||
|
expected: `qwer_y👍`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testCase := range cases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
replaced := ReplaceAllSymbols(testCase.str, testCase.set, testCase.replacer)
|
||||||
|
if replaced != testCase.expected {
|
||||||
|
t.Fatalf("Unexpected error:\nstr: %v set: %v replacer: %v\nGot: %v\nExpect %v\n", testCase.str, testCase.set, testCase.replacer, replaced, testCase.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
35
pkg/normalization/normalization.go
Normal file
35
pkg/normalization/normalization.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package normalization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProhibitedSymbolsStrict we can't use these symbols on Windows systems, but can use in *nix
|
||||||
|
var ProhibitedSymbolsStrict = regexp.MustCompilePOSIX(`[\\/:*?"<>|]`)
|
||||||
|
|
||||||
|
func NormalizeSpaceEnding(str string) (string, bool) {
|
||||||
|
var normalized bool
|
||||||
|
if string(str[len(str)-1]) == ` ` {
|
||||||
|
str = str[:len(str)-1] + `_`
|
||||||
|
normalized = true
|
||||||
|
}
|
||||||
|
return str, normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
func FullNormalize(str string) (string, bool) {
|
||||||
|
var normalized bool
|
||||||
|
s1 := ProhibitedSymbolsStrict.ReplaceAllString(str, `_`)
|
||||||
|
if s1 != str {
|
||||||
|
normalized = true
|
||||||
|
}
|
||||||
|
s2 := helpers.HandleCesu8(s1)
|
||||||
|
if s1 != s2 {
|
||||||
|
normalized = true
|
||||||
|
}
|
||||||
|
s3, n := NormalizeSpaceEnding(s2)
|
||||||
|
if n {
|
||||||
|
normalized = true
|
||||||
|
}
|
||||||
|
return s3, normalized
|
||||||
|
}
|
@ -2,6 +2,8 @@ package torrentStructures
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/rumanzo/bt2qbt/pkg/fileHelpers"
|
"github.com/rumanzo/bt2qbt/pkg/fileHelpers"
|
||||||
|
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
||||||
|
"github.com/rumanzo/bt2qbt/pkg/normalization"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,26 +14,49 @@ func (t *Torrent) IsV2OrHybryd() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFileListWB function that return struct with filelists with bytes from torrent file
|
func (t *Torrent) IsSingle() bool {
|
||||||
func (t *Torrent) GetFileListWB() []FilepathLength {
|
if t.Single != nil {
|
||||||
if t.FilePathLength == nil {
|
return *t.Single
|
||||||
if t.IsV2OrHybryd() { // torrents with v2 or hybrid scheme
|
}
|
||||||
result := getFileListV2(t.Info.FileTree)
|
single := false
|
||||||
t.FilePathLength = &result
|
if t.IsV2OrHybryd() {
|
||||||
return *t.FilePathLength
|
// v2 torrents always have at least one file that equal torrent name
|
||||||
} else { // torrent v1 with FileTree
|
if len(t.Info.FileTree) == 1 {
|
||||||
result := getFileListV1(t)
|
torrentName, _ := t.GetNormalizedTorrentName()
|
||||||
t.FilePathLength = &result
|
if _, ok := t.Info.FileTree[torrentName]; ok {
|
||||||
return *t.FilePathLength
|
single = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return *t.FilePathLength
|
if t.Info.Files == nil {
|
||||||
|
single = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Single = &single
|
||||||
|
return *t.Single
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileListWB function that return struct with filelists with bytes from torrent file
|
||||||
|
func (t *Torrent) GetFileListWB() ([]FilepathLength, bool) {
|
||||||
|
if t.FilePathLength == nil {
|
||||||
|
if t.IsV2OrHybryd() { // torrents with v2 or hybrid scheme
|
||||||
|
result, normalized := getFileListV2(t.Info.FileTree)
|
||||||
|
t.FilePathLength = &result
|
||||||
|
return *t.FilePathLength, normalized
|
||||||
|
} else { // torrent v1 with FileTree
|
||||||
|
result, normalized := getFileListV1(t)
|
||||||
|
t.FilePathLength = &result
|
||||||
|
return *t.FilePathLength, normalized
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return *t.FilePathLength, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) GetFileList() []string {
|
func (t *Torrent) GetFileList() ([]string, bool) {
|
||||||
|
var normalized bool
|
||||||
if t.FilePathLength == nil {
|
if t.FilePathLength == nil {
|
||||||
t.GetFileListWB()
|
_, normalized = t.GetFileListWB()
|
||||||
}
|
}
|
||||||
if t.FilePaths == nil {
|
if t.FilePaths == nil {
|
||||||
t.FilePaths = &[]string{}
|
t.FilePaths = &[]string{}
|
||||||
@ -39,29 +64,38 @@ func (t *Torrent) GetFileList() []string {
|
|||||||
*t.FilePaths = append(*t.FilePaths, fb.Path)
|
*t.FilePaths = append(*t.FilePaths, fb.Path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return *t.FilePaths
|
return *t.FilePaths, normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileListV1(t *Torrent) []FilepathLength {
|
func getFileListV1(t *Torrent) ([]FilepathLength, bool) {
|
||||||
|
var normalized bool
|
||||||
var files []FilepathLength
|
var files []FilepathLength
|
||||||
for _, file := range t.Info.Files {
|
for _, fileList := range t.Info.Files {
|
||||||
if file.PathUTF8 != nil {
|
|
||||||
files = append(files, FilepathLength{
|
var normalizedFileList []string
|
||||||
Path: fileHelpers.Join(file.PathUTF8, `/`),
|
if fileList.PathUTF8 != nil {
|
||||||
Length: file.Length,
|
normalizedFileList = fileList.PathUTF8
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
files = append(files, FilepathLength{
|
normalizedFileList = fileList.Path
|
||||||
Path: fileHelpers.Join(file.Path, `/`),
|
|
||||||
Length: file.Length,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
for index, filePathPart := range normalizedFileList {
|
||||||
|
normalizedFilePathPart, gotNormalized := normalization.FullNormalize(filePathPart)
|
||||||
|
if gotNormalized {
|
||||||
|
normalized = true
|
||||||
|
normalizedFileList[index] = normalizedFilePathPart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files = append(files, FilepathLength{
|
||||||
|
Path: fileHelpers.Join(normalizedFileList, `/`),
|
||||||
|
Length: fileList.Length,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return files
|
return files, normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileListV2(f interface{}) []FilepathLength {
|
func getFileListV2(f interface{}) ([]FilepathLength, bool) {
|
||||||
nfiles := []FilepathLength{}
|
var normalized bool
|
||||||
|
var nfiles []FilepathLength
|
||||||
|
|
||||||
// sort map previously
|
// sort map previously
|
||||||
keys := make([]string, 0, len(f.(map[string]interface{})))
|
keys := make([]string, 0, len(f.(map[string]interface{})))
|
||||||
@ -74,12 +108,39 @@ func getFileListV2(f interface{}) []FilepathLength {
|
|||||||
v := f.(map[string]interface{})[k]
|
v := f.(map[string]interface{})[k]
|
||||||
if len(k) == 0 { // it's means that next will be structure with length and piece root
|
if len(k) == 0 { // it's means that next will be structure with length and piece root
|
||||||
nfiles = append(nfiles, FilepathLength{Path: "", Length: v.(map[string]interface{})["length"].(int64)})
|
nfiles = append(nfiles, FilepathLength{Path: "", Length: v.(map[string]interface{})["length"].(int64)})
|
||||||
return nfiles
|
return nfiles, normalized
|
||||||
|
}
|
||||||
|
s, gotNormalized := getFileListV2(v)
|
||||||
|
if gotNormalized {
|
||||||
|
normalized = true
|
||||||
}
|
}
|
||||||
s := getFileListV2(v)
|
|
||||||
for _, fpl := range s {
|
for _, fpl := range s {
|
||||||
nfiles = append(nfiles, FilepathLength{Path: fileHelpers.Join(append([]string{k}, fpl.Path), `/`), Length: fpl.Length})
|
normalizedPath, gotNormalized := normalization.FullNormalize(k)
|
||||||
|
if gotNormalized {
|
||||||
|
normalized = true
|
||||||
|
}
|
||||||
|
nfiles = append(nfiles, FilepathLength{Path: fileHelpers.Join(append([]string{normalizedPath}, fpl.Path), `/`), Length: fpl.Length})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nfiles
|
return nfiles, normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Torrent) GetTorrentName() string {
|
||||||
|
if t.Info.NameUTF8 != "" {
|
||||||
|
return t.Info.NameUTF8
|
||||||
|
} else {
|
||||||
|
return t.Info.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Torrent) GetNormalizedTorrentName() (string, bool) {
|
||||||
|
torrentName := t.GetTorrentName()
|
||||||
|
var normalizedTorrentName string
|
||||||
|
var normalized bool
|
||||||
|
if fileHelpers.IsAbs(torrentName) {
|
||||||
|
normalizedTorrentName, normalized = normalization.NormalizeSpaceEnding(helpers.HandleCesu8(torrentName))
|
||||||
|
} else {
|
||||||
|
normalizedTorrentName, normalized = normalization.FullNormalize(torrentName)
|
||||||
|
}
|
||||||
|
return normalizedTorrentName, normalized
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,10 @@ func TestDecodeRealTorrents(t *testing.T) {
|
|||||||
name: "008 single v2",
|
name: "008 single v2",
|
||||||
path: "../../test/data/testfile1_single_v2.torrent",
|
path: "../../test/data/testfile1_single_v2.torrent",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "008 multi emoji prohibited symbols",
|
||||||
|
path: "../../test/data/tests_slahes_emoji_ut_created.torrent",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
@ -129,7 +133,7 @@ func TestTorrent_GetFileList(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error with decoding torrent file: %v", err)
|
t.Fatalf("Unexpected error with decoding torrent file: %v", err)
|
||||||
}
|
}
|
||||||
list := torrent.GetFileList()
|
list, _ := torrent.GetFileList()
|
||||||
equal := reflect.DeepEqual(list, testCase.expected)
|
equal := reflect.DeepEqual(list, testCase.expected)
|
||||||
if !equal && !testCase.mustFail {
|
if !equal && !testCase.mustFail {
|
||||||
changes, err := diff.Diff(list, testCase.expected, diff.DiscardComplexOrigin())
|
changes, err := diff.Diff(list, testCase.expected, diff.DiscardComplexOrigin())
|
||||||
@ -203,6 +207,21 @@ func TestTorrent_GetFileListWB(t *testing.T) {
|
|||||||
FilepathLength{Path: "testfile3.txt", Length: 33},
|
FilepathLength{Path: "testfile3.txt", Length: 33},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "004 testdir hybrid",
|
||||||
|
path: "../../test/data/testdir_hybrid.torrent",
|
||||||
|
expected: []FilepathLength{
|
||||||
|
FilepathLength{Path: "dir1/testfile1.txt", Length: 33},
|
||||||
|
FilepathLength{Path: "dir2/testfile1.txt", Length: 33},
|
||||||
|
FilepathLength{Path: "dir2/testfile2.txt", Length: 33},
|
||||||
|
FilepathLength{Path: "dir3/testfile1.txt", Length: 33},
|
||||||
|
FilepathLength{Path: "dir3/testfile2.txt", Length: 33},
|
||||||
|
FilepathLength{Path: "dir3/testfile3.txt", Length: 33},
|
||||||
|
FilepathLength{Path: "testfile1.txt", Length: 33},
|
||||||
|
FilepathLength{Path: "testfile2.txt", Length: 33},
|
||||||
|
FilepathLength{Path: "testfile3.txt", Length: 33},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
@ -211,7 +230,7 @@ func TestTorrent_GetFileListWB(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error with decoding torrent file: %v", err)
|
t.Fatalf("Unexpected error with decoding torrent file: %v", err)
|
||||||
}
|
}
|
||||||
list := torrent.GetFileListWB()
|
list, _ := torrent.GetFileListWB()
|
||||||
equal := reflect.DeepEqual(list, testCase.expected)
|
equal := reflect.DeepEqual(list, testCase.expected)
|
||||||
if !equal && !testCase.mustFail {
|
if !equal && !testCase.mustFail {
|
||||||
changes, err := diff.Diff(list, testCase.expected, diff.DiscardComplexOrigin())
|
changes, err := diff.Diff(list, testCase.expected, diff.DiscardComplexOrigin())
|
||||||
@ -225,3 +244,344 @@ func TestTorrent_GetFileListWB(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTorrent_GetFileListV1(t *testing.T) {
|
||||||
|
type TestCases struct {
|
||||||
|
name string
|
||||||
|
torrent *Torrent
|
||||||
|
expected []FilepathLength
|
||||||
|
expectedNormalized bool
|
||||||
|
mustFail bool
|
||||||
|
}
|
||||||
|
cases := []TestCases{
|
||||||
|
{
|
||||||
|
name: "001 emoji spaces at dir ends prohibited symbols",
|
||||||
|
torrent: &Torrent{Info: &TorrentInfo{
|
||||||
|
Files: []*TorrentFile{
|
||||||
|
&TorrentFile{Path: []string{"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"file_with/slash.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ", "file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ", "file_with/slash.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_space ", "file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_space ", "file_with/slash.txt"}, Length: 5},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedNormalized: true,
|
||||||
|
expected: []FilepathLength{
|
||||||
|
{Path: "file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_slash.txt", Length: 5},
|
||||||
|
},
|
||||||
|
mustFail: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "002 mustFail wrong filename",
|
||||||
|
torrent: &Torrent{Info: &TorrentInfo{
|
||||||
|
Files: []*TorrentFile{
|
||||||
|
&TorrentFile{Path: []string{"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"file_with/slash.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ", "file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ", "file_with/slash.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_space ", "file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_space ", "file_with/slash.txt"}, Length: 5},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedNormalized: true,
|
||||||
|
expected: []FilepathLength{
|
||||||
|
{Path: "file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file__with_slash.txt", Length: 5},
|
||||||
|
},
|
||||||
|
mustFail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "003 emoji spaces at dir ends prohibited symbols mustfail due returned false normalized",
|
||||||
|
torrent: &Torrent{Info: &TorrentInfo{
|
||||||
|
Files: []*TorrentFile{
|
||||||
|
&TorrentFile{Path: []string{"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"file_with/slash.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ", "file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ", "file_with/slash.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_space ", "file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir_with_space ", "file_with/slash.txt"}, Length: 5},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedNormalized: false,
|
||||||
|
expected: []FilepathLength{
|
||||||
|
{Path: "file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_slash.txt", Length: 5},
|
||||||
|
},
|
||||||
|
mustFail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "004 normal torrent",
|
||||||
|
torrent: &Torrent{Info: &TorrentInfo{
|
||||||
|
Files: []*TorrentFile{
|
||||||
|
&TorrentFile{Path: []string{"file.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir", "file.txt"}, Length: 5},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedNormalized: false,
|
||||||
|
expected: []FilepathLength{
|
||||||
|
{Path: "file.txt", Length: 5},
|
||||||
|
{Path: "testdir/file.txt", Length: 5},
|
||||||
|
},
|
||||||
|
mustFail: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testCase := range cases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
filePathLength, normalized := getFileListV1(testCase.torrent)
|
||||||
|
equal := reflect.DeepEqual(filePathLength, testCase.expected)
|
||||||
|
if !equal && !testCase.mustFail {
|
||||||
|
changes, err := diff.Diff(filePathLength, testCase.expected, diff.DiscardComplexOrigin())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
t.Fatalf("Unexpected error: structures isn't equal:\n Got: %#v\n Expect %#v\n Diff: %v\n", filePathLength, testCase.expected, spew.Sdump(changes))
|
||||||
|
}
|
||||||
|
if normalized != testCase.expectedNormalized && !testCase.mustFail {
|
||||||
|
t.Fatalf("Normalization expected %v, got %v", testCase.expectedNormalized, normalized)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestTorrent_GetFileListV2(t *testing.T) {
|
||||||
|
type TestCases struct {
|
||||||
|
name string
|
||||||
|
torrent *Torrent
|
||||||
|
expected []FilepathLength
|
||||||
|
expectedNormalized bool
|
||||||
|
mustFail bool
|
||||||
|
}
|
||||||
|
cases := []TestCases{
|
||||||
|
{
|
||||||
|
name: "001 emoji spaces at dir ends prohibited symbols",
|
||||||
|
torrent: &Torrent{Info: &TorrentInfo{
|
||||||
|
FileTree: map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ": map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"testdir_with_space ": map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedNormalized: true,
|
||||||
|
expected: []FilepathLength{
|
||||||
|
{Path: "file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
},
|
||||||
|
mustFail: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "002 mustFail wrong filename",
|
||||||
|
torrent: &Torrent{Info: &TorrentInfo{
|
||||||
|
FileTree: map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ": map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"testdir_with_space ": map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedNormalized: true,
|
||||||
|
expected: []FilepathLength{
|
||||||
|
{Path: "file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_emoji_ \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
},
|
||||||
|
mustFail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "003 emoji spaces at dir ends prohibited symbols mustfail due returned false normalized",
|
||||||
|
torrent: &Torrent{Info: &TorrentInfo{
|
||||||
|
FileTree: map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"testdir_with_emoji_and_space \xed\xa0\xbc\xed\xb6\x95 ": map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"testdir_with_space ": map[string]interface{}{
|
||||||
|
"file_with_emoji \xed\xa0\xbc\xed\xb6\x95.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"file_with/slash.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedNormalized: false,
|
||||||
|
expected: []FilepathLength{
|
||||||
|
{Path: "file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_emoji_and_space \xf0\x9f\x86\x95_/file_with_emoji \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_slash.txt", Length: 5},
|
||||||
|
{Path: "testdir_with_space_/file_with_emoji_ \xf0\x9f\x86\x95.txt", Length: 5},
|
||||||
|
},
|
||||||
|
mustFail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "004 normal torrent",
|
||||||
|
torrent: &Torrent{Info: &TorrentInfo{
|
||||||
|
Files: []*TorrentFile{
|
||||||
|
&TorrentFile{Path: []string{"file.txt"}, Length: 5},
|
||||||
|
&TorrentFile{Path: []string{"testdir", "file.txt"}, Length: 5},
|
||||||
|
},
|
||||||
|
FileTree: map[string]interface{}{
|
||||||
|
"file.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"testdir": map[string]interface{}{
|
||||||
|
"file.txt": map[string]interface{}{
|
||||||
|
``: map[string]interface{}{
|
||||||
|
`length`: int64(5),
|
||||||
|
`pieces_root`: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
expectedNormalized: false,
|
||||||
|
expected: []FilepathLength{
|
||||||
|
{Path: "file.txt", Length: 5},
|
||||||
|
{Path: "testdir/file.txt", Length: 5},
|
||||||
|
},
|
||||||
|
mustFail: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testCase := range cases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
filePathLength, normalized := getFileListV2(testCase.torrent.Info.FileTree)
|
||||||
|
equal := reflect.DeepEqual(filePathLength, testCase.expected)
|
||||||
|
if !equal && !testCase.mustFail {
|
||||||
|
changes, err := diff.Diff(filePathLength, testCase.expected, diff.DiscardComplexOrigin())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
t.Fatalf("Unexpected error: structures isn't equal:\n Got: %#v\nExpected: %#v\nDiff: %v\n", filePathLength, testCase.expected, spew.Sdump(changes))
|
||||||
|
}
|
||||||
|
if normalized != testCase.expectedNormalized && !testCase.mustFail {
|
||||||
|
t.Fatalf("Normalization expected %v, got %v", testCase.expectedNormalized, normalized)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ type Torrent struct {
|
|||||||
PieceLayers *map[string]interface{} `bencode:"piece layers"`
|
PieceLayers *map[string]interface{} `bencode:"piece layers"`
|
||||||
FilePathLength *[]FilepathLength `bencode:"-"` // service field
|
FilePathLength *[]FilepathLength `bencode:"-"` // service field
|
||||||
FilePaths *[]string `bencode:"-"` // service field
|
FilePaths *[]string `bencode:"-"` // service field
|
||||||
|
Single *bool `bencode:"-"` // service field
|
||||||
}
|
}
|
||||||
|
|
||||||
type TorrentInfo struct {
|
type TorrentInfo struct {
|
||||||
|
1
test/data/tests_slahes_emoji_ut_created.torrent
Normal file
1
test/data/tests_slahes_emoji_ut_created.torrent
Normal file
@ -0,0 +1 @@
|
|||||||
|
d8:announce44:udp://tracker.openbittorrent.com:80/announce13:announce-listll44:udp://tracker.openbittorrent.com:80/announceel42:udp://tracker.opentrackr.org:1337/announceee10:created by12:uTorrent/3.613:creation datei1701616527e8:encoding5:UTF-84:infod5:filesld6:lengthi8e4:pathl26:file_with_emoji_í ½í¸Š.txteed6:lengthi8e4:pathl19:file_with_slash.txteed6:lengthi8e4:pathl34:testdit_with_emoji_and_spaceí ½í¸Š26:file_with_emoji_í ½í¸Š.txteed6:lengthi8e4:pathl34:testdit_with_emoji_and_spaceí ½í¸Š19:file_with_slash.txteed6:lengthi8e4:pathl18:testdit_with_space26:file_with_emoji_í ½í¸Š.txteed6:lengthi8e4:pathl18:testdit_with_space19:file_with_slash.txteee4:name25:tests_slahes_emoji í ½í¸Š12:piece lengthi16384e6:pieces20:ûtYlYã<59><18>ìñÕdÙË<C399>wUee
|
Loading…
Reference in New Issue
Block a user