mirror of
https://github.com/rumanzo/bt2qbt.git
synced 2024-11-21 18:02:41 +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 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.
|
||||
|
||||
> [!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)
|
||||
- [Feature](#user-content-feature)
|
||||
|
@ -15,11 +15,6 @@ func (transfer *TransferStructure) HandleStructures() {
|
||||
|
||||
// if torrent name was renamed, add modified name
|
||||
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.AddedTime = transfer.ResumeItem.AddedOn
|
||||
transfer.Fastresume.CompletedTime = transfer.ResumeItem.CompletedOn
|
||||
@ -46,6 +41,6 @@ func (transfer *TransferStructure) HandleStructures() {
|
||||
transfer.NumPieces = int64(len(transfer.TorrentFile.Info.Pieces)) / 20
|
||||
|
||||
transfer.HandleCompleted() // important handle priorities before handling pieces
|
||||
transfer.HandleSavePaths()
|
||||
transfer.HandleSavePaths() // and there we handle torrent name also
|
||||
transfer.HandlePieces()
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/rumanzo/bt2qbt/internal/options"
|
||||
"io/ioutil"
|
||||
"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 {}
|
||||
var categoriesIsNew bool
|
||||
file, err := os.OpenFile(opts.Categories, os.O_RDWR, 0644)
|
||||
_, err := os.Stat(opts.Categories)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
categoriesIsNew = true
|
||||
} else if err != nil {
|
||||
@ -22,16 +21,11 @@ func ProcessLabels(opts *options.Opts, newtags []string) error {
|
||||
}
|
||||
|
||||
if !categoriesIsNew {
|
||||
dataRaw, err := ioutil.ReadAll(file)
|
||||
dataRaw, err := os.ReadFile(opts.Categories)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
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))
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(opts.Categories, newCategories, 0644)
|
||||
err = os.WriteFile(opts.Categories, newCategories, 0644)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Can't write categories.json. Error:\n%v\n", err))
|
||||
}
|
||||
|
@ -2,13 +2,12 @@ package transfer
|
||||
|
||||
import (
|
||||
"github.com/rumanzo/bt2qbt/internal/options"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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 {
|
||||
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 {
|
||||
if _, err := os.Stat(transferStructure.TorrentFilePath); os.IsNotExist(err) {
|
||||
for _, searchPath := range transferStructure.Opts.SearchPaths {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/rumanzo/bt2qbt/internal/replace"
|
||||
"github.com/rumanzo/bt2qbt/pkg/fileHelpers"
|
||||
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
||||
"github.com/rumanzo/bt2qbt/pkg/normalization"
|
||||
"github.com/rumanzo/bt2qbt/pkg/qBittorrentStructures"
|
||||
"github.com/rumanzo/bt2qbt/pkg/torrentStructures"
|
||||
"github.com/rumanzo/bt2qbt/pkg/utorrentStructs"
|
||||
@ -15,7 +16,6 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
//goland:noinspection GoNameStartsWithPackageName
|
||||
@ -82,13 +82,13 @@ func (transfer *TransferStructure) HandleCaption() {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
if transfer.ResumeItem.Started == 0 {
|
||||
transfer.Fastresume.Paused = 1
|
||||
transfer.Fastresume.AutoManaged = 0
|
||||
} else {
|
||||
if len(transfer.TorrentFile.GetFileList()) > 1 {
|
||||
if !transfer.TorrentFile.IsSingle() {
|
||||
var parted bool
|
||||
for _, prio := range transfer.Fastresume.FilePriority {
|
||||
if prio == 0 {
|
||||
@ -202,7 +202,7 @@ func (transfer *TransferStructure) HandlePieces() {
|
||||
if transfer.Fastresume.Unfinished != nil {
|
||||
transfer.FillWholePieces(0)
|
||||
} else {
|
||||
if len(transfer.TorrentFile.GetFileList()) > 0 {
|
||||
if !transfer.TorrentFile.IsSingle() {
|
||||
transfer.FillPiecesParted()
|
||||
} else {
|
||||
transfer.FillWholePieces(1)
|
||||
@ -227,7 +227,8 @@ func (transfer *TransferStructure) FillPiecesParted() {
|
||||
}
|
||||
var fileOffsets []Offset
|
||||
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
|
||||
bytesLength += file.Length
|
||||
fileLastOffset := bytesLength
|
||||
@ -267,31 +268,20 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
||||
// Original paths always ending with pathSeparator
|
||||
// SubFolder or NoSubfolder never have ending pathSeparator
|
||||
// qBtSavePath always has separator /, otherwise SavePath use os pathSeparator
|
||||
|
||||
if transfer.Magnet {
|
||||
transfer.Fastresume.QBtContentLayout = "Original"
|
||||
transfer.Fastresume.QbtSavePath = fileHelpers.Normalize(helpers.HandleCesu8(transfer.ResumeItem.Path), "/")
|
||||
} else {
|
||||
var torrentName string
|
||||
var torrentNameOriginal string
|
||||
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))
|
||||
var nameNormalized bool
|
||||
transfer.Fastresume.Name, nameNormalized = normalization.FullNormalize(transfer.TorrentFile.GetTorrentName())
|
||||
|
||||
if len(transfer.TorrentFile.GetFileList()) > 0 {
|
||||
var cesu8FilesExists bool
|
||||
for _, filePath := range transfer.TorrentFile.GetFileList() {
|
||||
cesuEncodedFilepath := helpers.HandleCesu8(filePath)
|
||||
if utf8.RuneCountInString(filePath) != utf8.RuneCountInString(cesuEncodedFilepath) {
|
||||
cesu8FilesExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if lastPathName == torrentName && !cesu8FilesExists {
|
||||
lastPathName := fileHelpers.Base(helpers.HandleCesu8(transfer.ResumeItem.Path))
|
||||
// if FileList contain only 1 file that means it is single file torrent
|
||||
if !transfer.TorrentFile.IsSingle() {
|
||||
fileList, filesNormalized := transfer.TorrentFile.GetFileList()
|
||||
|
||||
if lastPathName == transfer.Fastresume.Name && !filesNormalized && !nameNormalized {
|
||||
transfer.Fastresume.QBtContentLayout = "Original"
|
||||
transfer.Fastresume.QbtSavePath = fileHelpers.CutLastPath(helpers.HandleCesu8(transfer.ResumeItem.Path), transfer.Opts.PathSeparator)
|
||||
if maxIndex := transfer.FindHighestIndexOfMappedFiles(); maxIndex >= 0 {
|
||||
@ -309,7 +299,7 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
||||
pathParts[num] = helpers.HandleCesu8(part.(string))
|
||||
}
|
||||
// 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 {
|
||||
transfer.Fastresume.QBtContentLayout = "NoSubfolder"
|
||||
// NoSubfolder always has full mapped files
|
||||
// so we append all of them
|
||||
for _, filePath := range transfer.TorrentFile.GetFileList() {
|
||||
transfer.Fastresume.MappedFiles = append(transfer.Fastresume.MappedFiles, fileHelpers.Normalize(helpers.HandleCesu8(filePath), transfer.Opts.PathSeparator))
|
||||
// NoSubfolder always has full mapped files, so we append all of them
|
||||
for _, filePath := range fileList {
|
||||
transfer.Fastresume.MappedFiles = append(transfer.Fastresume.MappedFiles,
|
||||
fileHelpers.Normalize(filePath, transfer.Opts.PathSeparator))
|
||||
}
|
||||
// and then doing remap if resumeItem contain target field
|
||||
if maxIndex := transfer.FindHighestIndexOfMappedFiles(); maxIndex >= 0 {
|
||||
@ -344,7 +334,7 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
||||
}
|
||||
} else {
|
||||
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
|
||||
transfer.Fastresume.MappedFiles = []string{lastPathName}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent.txt`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -59,6 +60,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `E:/newfolder/`,
|
||||
SavePath: `E:\newfolder\`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent.txt`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -79,6 +81,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent.txt`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -107,6 +110,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent.txt`,
|
||||
MappedFiles: []string{`renamed_test_torrent.txt`},
|
||||
},
|
||||
},
|
||||
@ -135,6 +139,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `/mnt/d/torrents/`,
|
||||
SavePath: `/mnt/d/torrents/`,
|
||||
Name: `test_torrent.txt`,
|
||||
QBtContentLayout: "Original",
|
||||
MappedFiles: []string{`renamed_test_torrent.txt`},
|
||||
},
|
||||
@ -162,6 +167,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -187,6 +193,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `E:/newfolder/`,
|
||||
SavePath: `E:\newfolder\`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -212,6 +219,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -245,6 +253,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent`,
|
||||
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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `D:\torrents\test_torrent`,
|
||||
Targets: [][]interface{}{
|
||||
@ -283,6 +292,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `/mnt/d/torrents/`,
|
||||
SavePath: `/mnt/d/torrents/`,
|
||||
QBtContentLayout: "Original",
|
||||
Name: `test_torrent`,
|
||||
MappedFiles: []string{
|
||||
``,
|
||||
``,
|
||||
@ -294,7 +304,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
{
|
||||
name: "011 Test torrent with windows folder (NoSubfolder) path without replaces",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
||||
TorrentFile: &torrentStructures.Torrent{
|
||||
Info: &torrentStructures.TorrentInfo{
|
||||
@ -312,6 +322,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/torrents/test`,
|
||||
SavePath: `D:\torrents\test`,
|
||||
Name: `test_torrent`,
|
||||
MappedFiles: []string{
|
||||
`dir1\file1.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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
||||
TorrentFile: &torrentStructures.Torrent{
|
||||
Info: &torrentStructures.TorrentInfo{
|
||||
@ -342,6 +353,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `E:/newfolder/test`,
|
||||
SavePath: `E:\newfolder\test`,
|
||||
Name: `test_torrent`,
|
||||
MappedFiles: []string{
|
||||
`dir1\file1.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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test`},
|
||||
TorrentFile: &torrentStructures.Torrent{
|
||||
Info: &torrentStructures.TorrentInfo{
|
||||
@ -372,6 +384,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/torrents/test`,
|
||||
SavePath: `D:\torrents\test`,
|
||||
Name: `test_torrent`,
|
||||
MappedFiles: []string{
|
||||
`dir1\file1.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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `D:\torrents\test`,
|
||||
Targets: [][]interface{}{
|
||||
@ -411,6 +424,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
QbtSavePath: `D:/torrents/test`,
|
||||
SavePath: `D:\torrents\test`,
|
||||
QBtContentLayout: "NoSubfolder",
|
||||
Name: `test_torrent`,
|
||||
MappedFiles: []string{
|
||||
`dir1\file1.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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `D:\torrents\test`,
|
||||
Targets: [][]interface{}{
|
||||
@ -448,6 +462,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `/mnt/d/torrents/test`,
|
||||
SavePath: `/mnt/d/torrents/test`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "NoSubfolder",
|
||||
MappedFiles: []string{
|
||||
`dir1/file1.txt`,
|
||||
@ -478,6 +493,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/torrents/test`,
|
||||
SavePath: `D:\torrents\test`,
|
||||
Name: `test_torrent`,
|
||||
MappedFiles: []string{
|
||||
`dir1\file1.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",
|
||||
mustFail: true,
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent.txt`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{Path: `D:\torrents\test_torrent.txt`},
|
||||
TorrentFile: &torrentStructures.Torrent{
|
||||
Info: &torrentStructures.TorrentInfo{
|
||||
@ -504,6 +520,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torre`,
|
||||
Name: `test_torrent.txt`,
|
||||
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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `D:\torrents\test_torrent`,
|
||||
Targets: [][]interface{}{
|
||||
@ -547,6 +564,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "Original",
|
||||
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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `D:\torrents\test_torrent`,
|
||||
Targets: [][]interface{}{
|
||||
@ -597,6 +615,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `/mnt/d/torrents/`,
|
||||
SavePath: `/mnt/d/torrents/`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "Original",
|
||||
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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `D:\torrents\test`,
|
||||
Targets: [][]interface{}{
|
||||
@ -647,6 +666,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/torrents/test`,
|
||||
SavePath: `D:\torrents\test`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "NoSubfolder",
|
||||
MappedFiles: []string{
|
||||
`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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `\\torrents\test_torrent`,
|
||||
Targets: [][]interface{}{
|
||||
@ -697,6 +717,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `//torrents/`,
|
||||
SavePath: `\\torrents\`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "Original",
|
||||
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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `\\torrents\test`,
|
||||
Targets: [][]interface{}{
|
||||
@ -747,6 +768,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `//torrents/test`,
|
||||
SavePath: `\\torrents\test`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "NoSubfolder",
|
||||
MappedFiles: []string{
|
||||
`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",
|
||||
newTransferStructure: &TransferStructure{
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{Name: `test_torrent`},
|
||||
ResumeItem: &utorrentStructs.ResumeItem{
|
||||
Path: `\\torrents\test_torrent`,
|
||||
Targets: [][]interface{}{
|
||||
@ -797,6 +819,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `//torrents/`,
|
||||
SavePath: `//torrents/`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "Original",
|
||||
MappedFiles: []string{
|
||||
``,
|
||||
@ -847,6 +870,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `//torrents/test`,
|
||||
SavePath: `//torrents/test`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "NoSubfolder",
|
||||
MappedFiles: []string{
|
||||
`dir1/file1.txt`,
|
||||
@ -866,7 +890,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Path: `D:\torrents\test`,
|
||||
},
|
||||
TorrentFile: &torrentStructures.Torrent{
|
||||
Info: &torrentStructures.TorrentInfo{},
|
||||
Info: &torrentStructures.TorrentInfo{Name: "torrentname"},
|
||||
},
|
||||
Opts: &options.Opts{PathSeparator: `\`},
|
||||
Magnet: true,
|
||||
@ -875,6 +899,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/torrents/test`,
|
||||
SavePath: `D:\torrents\test`,
|
||||
Name: "torrentname",
|
||||
QBtContentLayout: "Original",
|
||||
},
|
||||
},
|
||||
@ -897,6 +922,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/`,
|
||||
SavePath: `D:\`,
|
||||
Name: `test.txt`,
|
||||
QBtContentLayout: "Original",
|
||||
},
|
||||
},
|
||||
@ -926,6 +952,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/`,
|
||||
SavePath: `D:\`,
|
||||
Name: `test_torrent`,
|
||||
QBtContentLayout: "Original",
|
||||
},
|
||||
},
|
||||
@ -967,6 +994,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
Name: "test_torrent \xf0\x9f\x86\x95",
|
||||
QBtContentLayout: "Original",
|
||||
MappedFiles: []string{
|
||||
"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{
|
||||
QbtSavePath: "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",
|
||||
MappedFiles: []string{
|
||||
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||
@ -1043,6 +1072,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: "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",
|
||||
MappedFiles: []string{
|
||||
"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{
|
||||
QbtSavePath: "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",
|
||||
MappedFiles: []string{
|
||||
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||
@ -1105,6 +1136,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: "D:/torrents/test_torrent",
|
||||
SavePath: "D:\\torrents\\test_torrent",
|
||||
Name: "test_torrent",
|
||||
QBtContentLayout: "NoSubfolder",
|
||||
MappedFiles: []string{
|
||||
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||
@ -1156,6 +1188,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||
QbtSavePath: "D:/torrents/test_torrent",
|
||||
SavePath: "D:\\torrents\\test_torrent",
|
||||
Name: "test_torrent",
|
||||
QBtContentLayout: "NoSubfolder",
|
||||
MappedFiles: []string{
|
||||
"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{
|
||||
QbtSavePath: `D:/torrents/`,
|
||||
SavePath: `D:\torrents\`,
|
||||
Name: "test_torrent \xf0\x9f\x86\x95.txt",
|
||||
MappedFiles: []string{
|
||||
"test_torrent \xf0\x9f\x86\x95.txt",
|
||||
},
|
||||
@ -1211,6 +1245,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
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`,
|
||||
@ -1220,6 +1255,435 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
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 {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
@ -1228,6 +1692,8 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
||||
testCase.newTransferStructure.Replace = replaces
|
||||
testCase.expected.Replace = replaces
|
||||
}
|
||||
testCase.newTransferStructure.Fastresume.Name, _ = testCase.newTransferStructure.TorrentFile.GetNormalizedTorrentName()
|
||||
testCase.newTransferStructure.HandleCaption()
|
||||
testCase.newTransferStructure.HandleSavePaths()
|
||||
equal := reflect.DeepEqual(testCase.expected.Fastresume, testCase.newTransferStructure.Fastresume)
|
||||
if !equal && !testCase.mustFail {
|
||||
|
@ -129,7 +129,7 @@ func TestJoin(t *testing.T) {
|
||||
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`},
|
||||
separator: `/`,
|
||||
expected: `testdir/my test file.txt`,
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"github.com/crazytyper/go-cesu8"
|
||||
"github.com/zeebo/bencode"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@ -35,7 +35,7 @@ func CheckExists(s string, arr []string) (bool, string) {
|
||||
}
|
||||
|
||||
func DecodeTorrentFile(path string, decodeTo interface{}) error {
|
||||
dat, err := ioutil.ReadFile(path)
|
||||
dat, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -119,3 +119,9 @@ func HandleCesu8(str string) string {
|
||||
}
|
||||
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) {
|
||||
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"
|
||||
@ -64,3 +65,71 @@ func TestEmojiCesu8(t *testing.T) {
|
||||
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 (
|
||||
"github.com/rumanzo/bt2qbt/pkg/fileHelpers"
|
||||
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
||||
"github.com/rumanzo/bt2qbt/pkg/normalization"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@ -12,26 +14,49 @@ func (t *Torrent) IsV2OrHybryd() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetFileListWB function that return struct with filelists with bytes from torrent file
|
||||
func (t *Torrent) GetFileListWB() []FilepathLength {
|
||||
if t.FilePathLength == nil {
|
||||
if t.IsV2OrHybryd() { // torrents with v2 or hybrid scheme
|
||||
result := getFileListV2(t.Info.FileTree)
|
||||
t.FilePathLength = &result
|
||||
return *t.FilePathLength
|
||||
} else { // torrent v1 with FileTree
|
||||
result := getFileListV1(t)
|
||||
t.FilePathLength = &result
|
||||
return *t.FilePathLength
|
||||
func (t *Torrent) IsSingle() bool {
|
||||
if t.Single != nil {
|
||||
return *t.Single
|
||||
}
|
||||
single := false
|
||||
if t.IsV2OrHybryd() {
|
||||
// v2 torrents always have at least one file that equal torrent name
|
||||
if len(t.Info.FileTree) == 1 {
|
||||
torrentName, _ := t.GetNormalizedTorrentName()
|
||||
if _, ok := t.Info.FileTree[torrentName]; ok {
|
||||
single = true
|
||||
}
|
||||
}
|
||||
} 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 {
|
||||
t.GetFileListWB()
|
||||
_, normalized = t.GetFileListWB()
|
||||
}
|
||||
if t.FilePaths == nil {
|
||||
t.FilePaths = &[]string{}
|
||||
@ -39,29 +64,38 @@ func (t *Torrent) GetFileList() []string {
|
||||
*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
|
||||
for _, file := range t.Info.Files {
|
||||
if file.PathUTF8 != nil {
|
||||
files = append(files, FilepathLength{
|
||||
Path: fileHelpers.Join(file.PathUTF8, `/`),
|
||||
Length: file.Length,
|
||||
})
|
||||
for _, fileList := range t.Info.Files {
|
||||
|
||||
var normalizedFileList []string
|
||||
if fileList.PathUTF8 != nil {
|
||||
normalizedFileList = fileList.PathUTF8
|
||||
} else {
|
||||
normalizedFileList = fileList.Path
|
||||
}
|
||||
for index, filePathPart := range normalizedFileList {
|
||||
normalizedFilePathPart, gotNormalized := normalization.FullNormalize(filePathPart)
|
||||
if gotNormalized {
|
||||
normalized = true
|
||||
normalizedFileList[index] = normalizedFilePathPart
|
||||
}
|
||||
}
|
||||
files = append(files, FilepathLength{
|
||||
Path: fileHelpers.Join(file.Path, `/`),
|
||||
Length: file.Length,
|
||||
Path: fileHelpers.Join(normalizedFileList, `/`),
|
||||
Length: fileList.Length,
|
||||
})
|
||||
}
|
||||
}
|
||||
return files
|
||||
return files, normalized
|
||||
}
|
||||
|
||||
func getFileListV2(f interface{}) []FilepathLength {
|
||||
nfiles := []FilepathLength{}
|
||||
func getFileListV2(f interface{}) ([]FilepathLength, bool) {
|
||||
var normalized bool
|
||||
var nfiles []FilepathLength
|
||||
|
||||
// sort map previously
|
||||
keys := make([]string, 0, len(f.(map[string]interface{})))
|
||||
@ -74,12 +108,39 @@ func getFileListV2(f interface{}) []FilepathLength {
|
||||
v := f.(map[string]interface{})[k]
|
||||
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)})
|
||||
return nfiles
|
||||
return nfiles, normalized
|
||||
}
|
||||
s, gotNormalized := getFileListV2(v)
|
||||
if gotNormalized {
|
||||
normalized = true
|
||||
}
|
||||
s := getFileListV2(v)
|
||||
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",
|
||||
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 {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
@ -129,7 +133,7 @@ func TestTorrent_GetFileList(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error with decoding torrent file: %v", err)
|
||||
}
|
||||
list := torrent.GetFileList()
|
||||
list, _ := torrent.GetFileList()
|
||||
equal := reflect.DeepEqual(list, testCase.expected)
|
||||
if !equal && !testCase.mustFail {
|
||||
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},
|
||||
},
|
||||
},
|
||||
{
|
||||
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 {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
@ -211,7 +230,7 @@ func TestTorrent_GetFileListWB(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error with decoding torrent file: %v", err)
|
||||
}
|
||||
list := torrent.GetFileListWB()
|
||||
list, _ := torrent.GetFileListWB()
|
||||
equal := reflect.DeepEqual(list, testCase.expected)
|
||||
if !equal && !testCase.mustFail {
|
||||
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"`
|
||||
FilePathLength *[]FilepathLength `bencode:"-"` // service field
|
||||
FilePaths *[]string `bencode:"-"` // service field
|
||||
Single *bool `bencode:"-"` // service field
|
||||
}
|
||||
|
||||
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