mirror of
https://github.com/rumanzo/bt2qbt.git
synced 2024-11-22 02:12:39 +01:00
Cesu8 (#44)
* cesu8 handler * resume file cesu8 handle * utf8 emoji filepaths * cesu8 emoji filepaths and torrent names * cesu8 emoji filepaths and torrent names handling improve tests * add fastresume Name field * update golang ver * handle torrent files with cesu8 encoded file paths and handle them as NoSubfolder torrents for better compatibility * fix cesu8 named single file torrents
This commit is contained in:
parent
a52ae9559e
commit
fda659db74
2
Makefile
2
Makefile
@ -1,4 +1,4 @@
|
|||||||
gotag=1.18.1-bullseye
|
gotag=1.21.4-bullseye
|
||||||
|
|
||||||
commit=$(shell git rev-parse HEAD)
|
commit=$(shell git rev-parse HEAD)
|
||||||
|
|
||||||
|
2
go.mod
2
go.mod
@ -3,9 +3,9 @@ module github.com/rumanzo/bt2qbt
|
|||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/crazytyper/go-cesu8 v0.0.0-20190615112902-270517b5a01c
|
||||||
github.com/davecgh/go-spew v1.1.0
|
github.com/davecgh/go-spew v1.1.0
|
||||||
github.com/fatih/color v1.13.0
|
github.com/fatih/color v1.13.0
|
||||||
github.com/go-ini/ini v1.64.0
|
|
||||||
github.com/jessevdk/go-flags v1.5.0
|
github.com/jessevdk/go-flags v1.5.0
|
||||||
github.com/r3labs/diff/v2 v2.15.0
|
github.com/r3labs/diff/v2 v2.15.0
|
||||||
github.com/stretchr/testify v1.7.0 // indirect
|
github.com/stretchr/testify v1.7.0 // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -1,9 +1,9 @@
|
|||||||
|
github.com/crazytyper/go-cesu8 v0.0.0-20190615112902-270517b5a01c h1:LslEy3hCBNp2TfgmcmJBIIAprB51yH+AIBC3kVDxlGc=
|
||||||
|
github.com/crazytyper/go-cesu8 v0.0.0-20190615112902-270517b5a01c/go.mod h1:eWhedTyAcrUdtMYyEjm6HmjjwSGRre54xWeBBZGhhYc=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/go-ini/ini v1.64.0 h1:73w/ADE+yoYjfu4BlI/LaEMe9Do1zOQ6qPt1du4uikI=
|
|
||||||
github.com/go-ini/ini v1.64.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package transfer
|
package transfer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -8,12 +9,17 @@ func (transfer *TransferStructure) HandleStructures() {
|
|||||||
|
|
||||||
if ok := transfer.ResumeItem.Targets; ok != nil {
|
if ok := transfer.ResumeItem.Targets; ok != nil {
|
||||||
for _, entry := range transfer.ResumeItem.Targets {
|
for _, entry := range transfer.ResumeItem.Targets {
|
||||||
transfer.Targets[entry[0].(int64)] = entry[1].(string)
|
transfer.Targets[entry[0].(int64)] = helpers.HandleCesu8(entry[1].(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -107,7 +107,7 @@ func HandleResumeItems(opts *options.Opts, resumeItems map[string]*utorrentStruc
|
|||||||
transferStruct.ResumeItem = resumeItem
|
transferStruct.ResumeItem = resumeItem
|
||||||
transferStruct.Replace = replaces
|
transferStruct.Replace = replaces
|
||||||
transferStruct.Opts = opts
|
transferStruct.Opts = opts
|
||||||
go HandleResumeItem(key, &transferStruct, &chans, &wg)
|
go HandleResumeItem(helpers.HandleCesu8(key), &transferStruct, &chans, &wg)
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
@ -4,7 +4,9 @@ import (
|
|||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/r3labs/diff/v2"
|
"github.com/r3labs/diff/v2"
|
||||||
"github.com/rumanzo/bt2qbt/internal/options"
|
"github.com/rumanzo/bt2qbt/internal/options"
|
||||||
|
"github.com/rumanzo/bt2qbt/pkg/fileHelpers"
|
||||||
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
"github.com/rumanzo/bt2qbt/pkg/helpers"
|
||||||
|
"github.com/rumanzo/bt2qbt/pkg/torrentStructures"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -134,6 +136,16 @@ func TestHandleTorrentFilePath(t *testing.T) {
|
|||||||
Opts: &options.Opts{BitDir: `C:\\temp`},
|
Opts: &options.Opts{BitDir: `C:\\temp`},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "007 Check emoji",
|
||||||
|
key: "normal_text \xf0\x9f\x86\x95 normal_text \xf0\x9f\x9a\x9c.txt.torrent",
|
||||||
|
newTransferStructure: &TransferStructure{Opts: &options.Opts{BitDir: `C:\\temp`}},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
TorrentFilePath: "C:/temp/normal_text \xf0\x9f\x86\x95 normal_text \xf0\x9f\x9a\x9c.txt.torrent",
|
||||||
|
TorrentFileName: "normal_text \xf0\x9f\x86\x95 normal_text \xf0\x9f\x9a\x9c.txt.torrent",
|
||||||
|
Opts: &options.Opts{BitDir: `C:\\temp`},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
@ -160,3 +172,23 @@ func TestPath(t *testing.T) {
|
|||||||
t.Fatalf("Can't decode torrent file with error: %v", err)
|
t.Fatalf("Can't decode torrent file with error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEmojiResumeDecode(t *testing.T) {
|
||||||
|
resumeFilePath := "../../test/data/resume_emoji_clear.dat"
|
||||||
|
torrentsDir := "../../test/data/"
|
||||||
|
resumeFile := map[string]interface{}{}
|
||||||
|
err := helpers.DecodeTorrentFile(resumeFilePath, resumeFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't decode torrent file with error: %v", err)
|
||||||
|
}
|
||||||
|
for k, _ := range resumeFile {
|
||||||
|
torrentFile := torrentStructures.Torrent{}
|
||||||
|
err := helpers.DecodeTorrentFile(fileHelpers.Join([]string{torrentsDir, helpers.HandleCesu8(k)}, "/"), &torrentFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't decode torrent file with error: %v", err)
|
||||||
|
}
|
||||||
|
if torrentFile.CreationDate == 0 {
|
||||||
|
t.Fatal("Decoded values is wrong")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
//goland:noinspection GoNameStartsWithPackageName
|
//goland:noinspection GoNameStartsWithPackageName
|
||||||
@ -76,7 +77,7 @@ func CreateEmptyNewTransferStructure() TransferStructure {
|
|||||||
|
|
||||||
func (transfer *TransferStructure) HandleCaption() {
|
func (transfer *TransferStructure) HandleCaption() {
|
||||||
if transfer.ResumeItem.Caption != "" {
|
if transfer.ResumeItem.Caption != "" {
|
||||||
transfer.Fastresume.QbtName = transfer.ResumeItem.Caption
|
transfer.Fastresume.QbtName = helpers.HandleCesu8(transfer.ResumeItem.Caption)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +128,7 @@ func (transfer *TransferStructure) HandleTags() {
|
|||||||
if transfer.Opts.WithoutTags == false && transfer.ResumeItem.Labels != nil {
|
if transfer.Opts.WithoutTags == false && transfer.ResumeItem.Labels != nil {
|
||||||
for _, label := range transfer.ResumeItem.Labels {
|
for _, label := range transfer.ResumeItem.Labels {
|
||||||
if label != "" {
|
if label != "" {
|
||||||
transfer.Fastresume.QbtTags = append(transfer.Fastresume.QbtTags, label)
|
transfer.Fastresume.QbtTags = append(transfer.Fastresume.QbtTags, helpers.HandleCesu8(label))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -136,7 +137,7 @@ func (transfer *TransferStructure) HandleTags() {
|
|||||||
}
|
}
|
||||||
func (transfer *TransferStructure) HandleLabels() {
|
func (transfer *TransferStructure) HandleLabels() {
|
||||||
if transfer.Opts.WithoutLabels == false {
|
if transfer.Opts.WithoutLabels == false {
|
||||||
transfer.Fastresume.QBtCategory = transfer.ResumeItem.Label
|
transfer.Fastresume.QBtCategory = helpers.HandleCesu8(transfer.ResumeItem.Label)
|
||||||
} else {
|
} else {
|
||||||
transfer.Fastresume.QBtCategory = ""
|
transfer.Fastresume.QBtCategory = ""
|
||||||
}
|
}
|
||||||
@ -155,9 +156,9 @@ func (transfer *TransferStructure) HandleTrackers() {
|
|||||||
index = "main"
|
index = "main"
|
||||||
}
|
}
|
||||||
if _, ok := trackersMap[index]; ok {
|
if _, ok := trackersMap[index]; ok {
|
||||||
trackersMap[index] = append(trackersMap[index], tracker)
|
trackersMap[index] = append(trackersMap[index], helpers.HandleCesu8(tracker))
|
||||||
} else {
|
} else {
|
||||||
trackersMap[index] = []string{tracker}
|
trackersMap[index] = []string{helpers.HandleCesu8(tracker)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if val, ok := trackersMap["main"]; ok {
|
if val, ok := trackersMap["main"]; ok {
|
||||||
@ -268,40 +269,51 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
|||||||
// 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(transfer.ResumeItem.Path, "/")
|
transfer.Fastresume.QbtSavePath = fileHelpers.Normalize(helpers.HandleCesu8(transfer.ResumeItem.Path), "/")
|
||||||
} else {
|
} else {
|
||||||
var torrentName string
|
var torrentName string
|
||||||
|
var torrentNameOriginal string
|
||||||
if transfer.TorrentFile.Info.NameUTF8 != "" {
|
if transfer.TorrentFile.Info.NameUTF8 != "" {
|
||||||
torrentName = transfer.TorrentFile.Info.NameUTF8
|
torrentName = helpers.HandleCesu8(transfer.TorrentFile.Info.NameUTF8)
|
||||||
|
torrentNameOriginal = transfer.TorrentFile.Info.NameUTF8
|
||||||
} else {
|
} else {
|
||||||
torrentName = transfer.TorrentFile.Info.Name
|
torrentName = helpers.HandleCesu8(transfer.TorrentFile.Info.Name)
|
||||||
|
torrentNameOriginal = transfer.TorrentFile.Info.Name
|
||||||
}
|
}
|
||||||
lastPathName := fileHelpers.Base(transfer.ResumeItem.Path)
|
lastPathName := fileHelpers.Base(helpers.HandleCesu8(transfer.ResumeItem.Path))
|
||||||
|
|
||||||
if len(transfer.TorrentFile.GetFileList()) > 0 {
|
if len(transfer.TorrentFile.GetFileList()) > 0 {
|
||||||
if lastPathName == torrentName {
|
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 {
|
||||||
transfer.Fastresume.QBtContentLayout = "Original"
|
transfer.Fastresume.QBtContentLayout = "Original"
|
||||||
transfer.Fastresume.QbtSavePath = fileHelpers.CutLastPath(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 {
|
||||||
transfer.Fastresume.MappedFiles = make([]string, maxIndex+1, maxIndex+1)
|
transfer.Fastresume.MappedFiles = make([]string, maxIndex+1, maxIndex+1)
|
||||||
for _, paths := range transfer.ResumeItem.Targets {
|
for _, paths := range transfer.ResumeItem.Targets {
|
||||||
index := paths[0].(int64)
|
index := paths[0].(int64)
|
||||||
var pathParts []string
|
var pathParts []string
|
||||||
if fileHelpers.IsAbs(paths[1].(string)) {
|
if fileHelpers.IsAbs(helpers.HandleCesu8(paths[1].(string))) {
|
||||||
pathParts = []string{fileHelpers.Normalize(paths[1].(string), transfer.Opts.PathSeparator)}
|
pathParts = []string{fileHelpers.Normalize(helpers.HandleCesu8(paths[1].(string)), transfer.Opts.PathSeparator)}
|
||||||
// if path is absolute just normalize it
|
// if path is absolute just normalize it
|
||||||
transfer.Fastresume.MappedFiles[index] = fileHelpers.Join(pathParts, transfer.Opts.PathSeparator)
|
transfer.Fastresume.MappedFiles[index] = fileHelpers.Join(pathParts, transfer.Opts.PathSeparator)
|
||||||
} else {
|
} else {
|
||||||
pathParts = make([]string, len(paths)-1, len(paths)-1)
|
pathParts = make([]string, len(paths)-1, len(paths)-1)
|
||||||
for num, part := range paths[1:] {
|
for num, part := range paths[1:] {
|
||||||
pathParts[num] = 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{torrentName}, pathParts...), transfer.Opts.PathSeparator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transfer.Fastresume.QbtSavePath = fileHelpers.CutLastPath(transfer.ResumeItem.Path, "/")
|
transfer.Fastresume.QbtSavePath = fileHelpers.CutLastPath(helpers.HandleCesu8(transfer.ResumeItem.Path), "/")
|
||||||
if string(transfer.Fastresume.QbtSavePath[len(transfer.Fastresume.QbtSavePath)-1]) != `/` {
|
if string(transfer.Fastresume.QbtSavePath[len(transfer.Fastresume.QbtSavePath)-1]) != `/` {
|
||||||
transfer.Fastresume.QbtSavePath += `/`
|
transfer.Fastresume.QbtSavePath += `/`
|
||||||
}
|
}
|
||||||
@ -310,33 +322,33 @@ func (transfer *TransferStructure) HandleSavePaths() {
|
|||||||
// 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 transfer.TorrentFile.GetFileList() {
|
for _, filePath := range transfer.TorrentFile.GetFileList() {
|
||||||
transfer.Fastresume.MappedFiles = append(transfer.Fastresume.MappedFiles, fileHelpers.Normalize(filePath, transfer.Opts.PathSeparator))
|
transfer.Fastresume.MappedFiles = append(transfer.Fastresume.MappedFiles, fileHelpers.Normalize(helpers.HandleCesu8(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 {
|
||||||
for _, paths := range transfer.ResumeItem.Targets {
|
for _, paths := range transfer.ResumeItem.Targets {
|
||||||
index := paths[0].(int64)
|
index := paths[0].(int64)
|
||||||
var pathParts []string
|
var pathParts []string
|
||||||
if fileHelpers.IsAbs(paths[1].(string)) {
|
if fileHelpers.IsAbs(helpers.HandleCesu8(paths[1].(string))) {
|
||||||
pathParts = []string{fileHelpers.Normalize(paths[1].(string), transfer.Opts.PathSeparator)}
|
pathParts = []string{fileHelpers.Normalize(helpers.HandleCesu8(paths[1].(string)), transfer.Opts.PathSeparator)}
|
||||||
} else {
|
} else {
|
||||||
pathParts = make([]string, len(paths)-1, len(paths)-1)
|
pathParts = make([]string, len(paths)-1, len(paths)-1)
|
||||||
for num, part := range paths[1:] {
|
for num, part := range paths[1:] {
|
||||||
pathParts[num] = part.(string)
|
pathParts[num] = helpers.HandleCesu8(part.(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transfer.Fastresume.MappedFiles[index] = fileHelpers.Join(pathParts, transfer.Opts.PathSeparator)
|
transfer.Fastresume.MappedFiles[index] = fileHelpers.Join(pathParts, transfer.Opts.PathSeparator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transfer.Fastresume.QbtSavePath = fileHelpers.Normalize(transfer.ResumeItem.Path, "/")
|
transfer.Fastresume.QbtSavePath = fileHelpers.Normalize(helpers.HandleCesu8(transfer.ResumeItem.Path), "/")
|
||||||
}
|
}
|
||||||
} 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 != torrentName {
|
if lastPathName != torrentNameOriginal {
|
||||||
//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}
|
||||||
}
|
}
|
||||||
transfer.Fastresume.QbtSavePath = fileHelpers.CutLastPath(transfer.ResumeItem.Path, `/`)
|
transfer.Fastresume.QbtSavePath = fileHelpers.CutLastPath(helpers.HandleCesu8(transfer.ResumeItem.Path), `/`)
|
||||||
if string(transfer.Fastresume.QbtSavePath[len(transfer.Fastresume.QbtSavePath)-1]) != `/` {
|
if string(transfer.Fastresume.QbtSavePath[len(transfer.Fastresume.QbtSavePath)-1]) != `/` {
|
||||||
transfer.Fastresume.QbtSavePath += `/`
|
transfer.Fastresume.QbtSavePath += `/`
|
||||||
}
|
}
|
||||||
|
@ -659,7 +659,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "021 Test torrent with windows 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{},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
@ -709,7 +709,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "022 Test torrent with windows 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{},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
@ -902,7 +902,7 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "027 Test torrent with signle file torrent and savepath in rootdirectory",
|
name: "027 Test torrent with multi file torrent and savepath in rootdirectory",
|
||||||
newTransferStructure: &TransferStructure{
|
newTransferStructure: &TransferStructure{
|
||||||
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
ResumeItem: &utorrentStructs.ResumeItem{
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
@ -930,6 +930,267 @@ func TestTransferStructure_HandleSavePaths(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "028 Test torrent with windows folder (original) path without replaces. Emoji utf8 in file and name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: "D:\\torrents\\test_torrent \xf0\x9f\x86\x95",
|
||||||
|
Targets: [][]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
int64(0),
|
||||||
|
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
int64(1),
|
||||||
|
"\\\\somedir\\somedir4 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent3.txt",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
int64(2),
|
||||||
|
"renamed \xf0\x9f\x86\x95 file1.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent \xf0\x9f\x86\x95",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "\xf0\x9f\x86\x95 file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"\xf0\x9f\x86\x95", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0 \xf0\x9f\x86\x95.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/torrents/`,
|
||||||
|
SavePath: `D:\torrents\`,
|
||||||
|
QBtContentLayout: "Original",
|
||||||
|
MappedFiles: []string{
|
||||||
|
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
||||||
|
"\\\\somedir\\somedir4 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent3.txt",
|
||||||
|
"test_torrent \xf0\x9f\x86\x95\\renamed \xf0\x9f\x86\x95 file1.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "029 Test torrent with windows folder (NoSubfolder) with renamed files. Emoji utf8 in file and torrent name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{Path: "D:\\torrents\\renamed test_torrent \xf0\x9f\x86\x95"},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent \xf0\x9f\x86\x95",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "\xf0\x9f\x86\x95 file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"\xf0\x9f\x86\x95", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0 \xf0\x9f\x86\x95.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: "D:/torrents/renamed test_torrent \xf0\x9f\x86\x95",
|
||||||
|
SavePath: "D:\\torrents\\renamed test_torrent \xf0\x9f\x86\x95",
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||||
|
"\xf0\x9f\x86\x95\\file2.txt",
|
||||||
|
"file0 \xf0\x9f\x86\x95.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "030 Test torrent with windows folder (original) path with renamed files. Emoji cesu8 in file and name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: "D:\\torrents\\test_torrent \xed\xa0\xbc\xed\xb6\x95",
|
||||||
|
Targets: [][]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
int64(0),
|
||||||
|
"E:\\somedir1 \xed\xa0\xbc\xed\xb6\x95\\\xed\xa0\xbc\xed\xb6\x95 renamed_test_torrent2.txt",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
int64(1),
|
||||||
|
"\\\\somedir\\somedir4 \xed\xa0\xbc\xed\xb6\x95\\\xed\xa0\xbc\xed\xb6\x95 renamed_test_torrent3.txt",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
int64(2),
|
||||||
|
"renamed \xed\xa0\xbc\xed\xb6\x95 file1.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent \xed\xa0\xbc\xed\xb6\x95",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "\xed\xa0\xbc\xed\xb6\x95 file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"\xed\xa0\xbc\xed\xb6\x95", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0 \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: "D:/torrents/test_torrent \xf0\x9f\x86\x95",
|
||||||
|
SavePath: "D:\\torrents\\test_torrent \xf0\x9f\x86\x95",
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
||||||
|
"\\\\somedir\\somedir4 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent3.txt",
|
||||||
|
"renamed \xf0\x9f\x86\x95 file1.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "031 Test torrent with windows folder (NoSubfolder) with renamed files. Emoji cesu8 in file and torrent name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{Path: "D:\\torrents\\renamed test_torrent \xed\xa0\xbc\xed\xb6\x95"},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent \xed\xa0\xbc\xed\xb6\x95",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "\xed\xa0\xbc\xed\xb6\x95 file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"\xed\xa0\xbc\xed\xb6\x95", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0 \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: "D:/torrents/renamed test_torrent \xf0\x9f\x86\x95",
|
||||||
|
SavePath: "D:\\torrents\\renamed test_torrent \xf0\x9f\x86\x95",
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||||
|
"\xf0\x9f\x86\x95\\file2.txt",
|
||||||
|
"file0 \xf0\x9f\x86\x95.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "032 Test torrent with windows folder without renamed files. Emoji cesu8 in file and torrent name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{Path: "D:\\torrents\\test_torrent"},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "\xed\xa0\xbc\xed\xb6\x95 file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"\xed\xa0\xbc\xed\xb6\x95", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0 \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file2 \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: "D:/torrents/test_torrent",
|
||||||
|
SavePath: "D:\\torrents\\test_torrent",
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
"dir1\\\xf0\x9f\x86\x95 file1.txt",
|
||||||
|
"\xf0\x9f\x86\x95\\file2.txt",
|
||||||
|
"file0 \xf0\x9f\x86\x95.txt",
|
||||||
|
`file1.txt`,
|
||||||
|
"file2 \xf0\x9f\x86\x95.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "033 Test torrent with windows folder with renamed files. Emoji cesu8 in file and torrent name",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{
|
||||||
|
Path: "D:\\torrents\\test_torrent",
|
||||||
|
Targets: [][]interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
int64(0),
|
||||||
|
"E:\\somedir1 \xed\xa0\xbc\xed\xb6\x95\\\xed\xa0\xbc\xed\xb6\x95 renamed_test_torrent2.txt",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
int64(1),
|
||||||
|
"\\\\somedir\\somedir4 \xed\xa0\xbc\xed\xb6\x95\\\xed\xa0\xbc\xed\xb6\x95 renamed_test_torrent3.txt",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
int64(3),
|
||||||
|
"renamed \xed\xa0\xbc\xed\xb6\x95 file1.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent",
|
||||||
|
Files: []*torrentStructures.TorrentFile{
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"dir1", "\xed\xa0\xbc\xed\xb6\x95 file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"\xed\xa0\xbc\xed\xb6\x95", "file2.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file0 \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file1.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file2 \xed\xa0\xbc\xed\xb6\x95.txt"}},
|
||||||
|
&torrentStructures.TorrentFile{Path: []string{"file4.txt"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: "D:/torrents/test_torrent",
|
||||||
|
SavePath: "D:\\torrents\\test_torrent",
|
||||||
|
QBtContentLayout: "NoSubfolder",
|
||||||
|
MappedFiles: []string{
|
||||||
|
"E:\\somedir1 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent2.txt",
|
||||||
|
"\\\\somedir\\somedir4 \xf0\x9f\x86\x95\\\xf0\x9f\x86\x95 renamed_test_torrent3.txt",
|
||||||
|
"file0 \xf0\x9f\x86\x95.txt",
|
||||||
|
"renamed \xf0\x9f\x86\x95 file1.txt",
|
||||||
|
"file2 \xf0\x9f\x86\x95.txt",
|
||||||
|
"file4.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "034 Test torrent with windows single nofolder (original) path without replaces. Cesu8 emoji",
|
||||||
|
newTransferStructure: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{},
|
||||||
|
ResumeItem: &utorrentStructs.ResumeItem{Path: "D:\\torrents\\test_torrent \xed\xa0\xbc\xed\xb6\x95.txt"},
|
||||||
|
TorrentFile: &torrentStructures.Torrent{
|
||||||
|
Info: &torrentStructures.TorrentInfo{
|
||||||
|
Name: "test_torrent \xed\xa0\xbc\xed\xb6\x95.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Opts: &options.Opts{PathSeparator: `\`},
|
||||||
|
},
|
||||||
|
expected: &TransferStructure{
|
||||||
|
Fastresume: &qBittorrentStructures.QBittorrentFastresume{
|
||||||
|
QbtSavePath: `D:/torrents/`,
|
||||||
|
SavePath: `D:\torrents\`,
|
||||||
|
MappedFiles: []string{
|
||||||
|
"test_torrent \xf0\x9f\x86\x95.txt",
|
||||||
|
},
|
||||||
|
QBtContentLayout: "Original",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
@ -3,6 +3,7 @@ package helpers
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"github.com/crazytyper/go-cesu8"
|
||||||
"github.com/zeebo/bencode"
|
"github.com/zeebo/bencode"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -111,3 +112,10 @@ func GetStrings(trackers interface{}) []string {
|
|||||||
}
|
}
|
||||||
return ntrackers
|
return ntrackers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HandleCesu8(str string) string {
|
||||||
|
if strings.Contains(str, "\xed\xa0") {
|
||||||
|
return cesu8.DecodeString([]byte(str))
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
@ -57,3 +57,10 @@ 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"
|
||||||
|
if utf8 != HandleCesu8(cesu8) {
|
||||||
|
t.Fatalf("Cesu8 to utf-8 transformation fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -30,6 +30,7 @@ type QBittorrentFastresume struct {
|
|||||||
MappedFiles []string `bencode:"mapped_files,omitempty"` // list of strings. If any file in the torrent has been renamed, this entry contains a list of all the filenames. In the same order as in the torrent file.
|
MappedFiles []string `bencode:"mapped_files,omitempty"` // list of strings. If any file in the torrent has been renamed, this entry contains a list of all the filenames. In the same order as in the torrent file.
|
||||||
MaxConnections int64 `bencode:"max_connections"` // integer. The max number of peer connections this torrent may have, if a limit is set.
|
MaxConnections int64 `bencode:"max_connections"` // integer. The max number of peer connections this torrent may have, if a limit is set.
|
||||||
MaxUploads int64 `bencode:"max_uploads"` // integer. The max number of unchoked peers this torrent may have, if a limit is set.
|
MaxUploads int64 `bencode:"max_uploads"` // integer. The max number of unchoked peers this torrent may have, if a limit is set.
|
||||||
|
Name string `bencode:"name"`
|
||||||
NumComplete int64 `bencode:"num_complete"`
|
NumComplete int64 `bencode:"num_complete"`
|
||||||
NumDownloaded int64 `bencode:"num_downloaded"`
|
NumDownloaded int64 `bencode:"num_downloaded"`
|
||||||
NumIncomplete int64 `bencode:"num_incomplete"`
|
NumIncomplete int64 `bencode:"num_incomplete"`
|
||||||
|
1
test/data/normal_text 🆕 normal_text 🚜.txt.torrent
Normal file
1
test/data/normal_text 🆕 normal_text 🚜.txt.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 datei1700849684e8:encoding5:UTF-84:infod6:lengthi4e4:name41:normal_text í ¼í¶• normal_text í ½íºœ.txt12:piece lengthi16384e6:pieces20:©J<C2A9>å̱›¦Lsӑ釘/»Óee
|
1
test/data/resume_emoji_clear.dat
Normal file
1
test/data/resume_emoji_clear.dat
Normal file
@ -0,0 +1 @@
|
|||||||
|
d49:normal_text <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> normal_text <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.txt.torrentd4:test4:testee
|
Loading…
Reference in New Issue
Block a user