Skip to content

Commit

Permalink
feat: torrent file support on web (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
monkeyWie authored Jul 28, 2023
1 parent 81518d7 commit a5d8713
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 55 deletions.
12 changes: 11 additions & 1 deletion internal/protocol/bt/fetcher.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bt

import (
"bytes"
"github.com/GopeedLab/gopeed/internal/controller"
"github.com/GopeedLab/gopeed/internal/fetcher"
"github.com/GopeedLab/gopeed/pkg/base"
Expand Down Expand Up @@ -223,6 +224,15 @@ func (f *Fetcher) addTorrent(req *base.Request) (err error) {
schema := util.ParseSchema(req.URL)
if schema == "MAGNET" {
f.torrent, err = client.AddMagnet(req.URL)
} else if schema == "APPLICATION/X-BITTORRENT" {
_, data := util.ParseDataUri(req.URL)
buf := bytes.NewBuffer(data)
var metaInfo *metainfo.MetaInfo
metaInfo, err = metainfo.Load(buf)
if err != nil {
return err
}
f.torrent, err = client.AddTorrent(metaInfo)
} else {
f.torrent, err = client.AddTorrentFromFile(req.URL)
}
Expand Down Expand Up @@ -265,7 +275,7 @@ func (f *Fetcher) addTorrent(req *base.Request) (err error) {
type FetcherBuilder struct {
}

var schemes = []string{"FILE", "MAGNET"}
var schemes = []string{"FILE", "MAGNET", "APPLICATION/X-BITTORRENT"}

func (fb *FetcherBuilder) Schemes() []string {
return schemes
Expand Down
35 changes: 35 additions & 0 deletions internal/protocol/bt/fetcher_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package bt

import (
"encoding/base64"
"encoding/json"
"github.com/GopeedLab/gopeed/internal/controller"
"github.com/GopeedLab/gopeed/internal/fetcher"
"github.com/GopeedLab/gopeed/internal/test"
"github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/protocol/bt"
"os"
"reflect"
"testing"
)
Expand All @@ -15,6 +17,39 @@ func TestFetcher_Resolve_Torrent(t *testing.T) {
doResolve(t, buildFetcher())
}

func TestFetcher_Resolve_DataUri_Torrent(t *testing.T) {
fetcher := buildFetcher()
buf, err := os.ReadFile("./testdata/ubuntu-22.04-live-server-amd64.iso.torrent")
if err != nil {
t.Fatal(err)
}
// convert to data uri
dataUri := "data:application/x-bittorrent;base64," + base64.StdEncoding.EncodeToString(buf)
err = fetcher.Resolve(&base.Request{
URL: dataUri,
})
if err != nil {
panic(err)
}

want := &base.Resource{
Name: "ubuntu-22.04-live-server-amd64.iso",
Size: 1466714112,
Range: true,
RootDir: "ubuntu-22.04-live-server-amd64.iso",
Files: []*base.FileInfo{
{
Name: "ubuntu-22.04-live-server-amd64.iso",
Size: 1466714112,
},
},
Hash: "8a55cfbd5ca5d11507364765936c4f9e55b253ed",
}
if !reflect.DeepEqual(want, fetcher.Meta().Res) {
t.Errorf("Resolve() got = %v, want %v", fetcher.Meta().Res, want)
}
}

func TestFetcher_Config(t *testing.T) {
doResolve(t, buildConfigFetcher())
}
Expand Down
3 changes: 0 additions & 3 deletions pkg/download/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,6 @@ func (d *Downloader) Setup() error {
func (d *Downloader) parseFb(url string) (fetcher.FetcherBuilder, error) {
schema := util.ParseSchema(url)
fetchBuilder, ok := d.fetchBuilders[schema]
if !ok {
fetchBuilder, ok = d.fetchBuilders[util.FileSchema]
}
if ok {
return fetchBuilder, nil
}
Expand Down
10 changes: 10 additions & 0 deletions pkg/util/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,13 @@ func CreateDirIfNotExist(dir string) error {
}
return nil
}

// IsExistsFile check file exists and is a file
func IsExistsFile(path string) bool {
info, err := os.Stat(path)
// 判断路径是否存在,并且是一个文件
if err == nil && !info.IsDir() {
return true
}
return false
}
40 changes: 40 additions & 0 deletions pkg/util/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,43 @@ func doCheckDuplicateAndRename(t *testing.T, exitsPaths []string, path string, e
t.Errorf("CheckDuplicateAndRename() = %v, want %v", got, except)
}
}

func TestIsExistsFile(t *testing.T) {
type args struct {
path string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "exist",
args: args{
path: "./path.go",
},
want: true,
},
{
name: "not exist",
args: args{
path: "./path_not_exist.go",
},
want: false,
},
{
name: "is dir",
args: args{
path: "../util",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsExistsFile(tt.args.path); got != tt.want {
t.Errorf("IsExistsFile() = %v, want %v", got, tt.want)
}
})
}
}
35 changes: 32 additions & 3 deletions pkg/util/url.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@
package util

import "strings"
import (
"encoding/base64"
"regexp"
"strings"
)

const FileSchema = "FILE"

func ParseSchema(url string) string {
index := strings.Index(url, ":")
if index == -1 {
return FileSchema
// check is file path
if IsExistsFile(url) {
return FileSchema
}
return ""
}
return strings.ToUpper(url[:index])
schema := url[:index]
if schema == "data" {
schema, _ = ParseDataUri(url)
}
return strings.ToUpper(schema)
}

// ParseDataUri parses a data URI and returns the MIME type and decode data.
func ParseDataUri(uri string) (string, []byte) {
re := regexp.MustCompile(`^data:(.*);base64,(.*)$`)
matches := re.FindStringSubmatch(uri)
if len(matches) != 3 {
return "", nil
}
mime := matches[1]
base64Data := matches[2]
// 解码Base64数据
data, err := base64.StdEncoding.DecodeString(base64Data)
if err != nil {
return "", nil
}
return mime, data
}
84 changes: 82 additions & 2 deletions pkg/util/url_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package util

import "testing"
import (
"encoding/base64"
"reflect"
"testing"
)

func TestParseSchema(t *testing.T) {
type args struct {
Expand Down Expand Up @@ -32,13 +36,27 @@ func TestParseSchema(t *testing.T) {
},
want: "FILE",
},
{
name: "file-not-exists",
args: args{
url: "not.txt",
},
want: "",
},
{
name: "file-no-scheme",
args: args{
url: "/home/bt.torrent",
url: "./url.go",
},
want: "FILE",
},
{
name: "data-uri",
args: args{
url: "data:application/x-bittorrent;base64,test",
},
want: "APPLICATION/X-BITTORRENT",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -48,3 +66,65 @@ func TestParseSchema(t *testing.T) {
})
}
}

func TestParseDataUri(t *testing.T) {
type args struct {
uri string
}
type result struct {
mime string
data []byte
}

testData := []byte("test")
testData64 := base64.StdEncoding.EncodeToString(testData)

tests := []struct {
name string
args args
want result
}{
{
name: "success",
args: args{
uri: "data:application/x-bittorrent;base64," + testData64,
},
want: result{
mime: "application/x-bittorrent",
data: testData,
},
},
{
name: "fail-dirty-data",
args: args{
uri: "data::application/x-bittorrent;base64,!@$",
},
want: result{
mime: "",
data: nil,
},
},
{
name: "fail-miss-data",
args: args{
uri: ":application/x-bittorrent;base64," + testData64,
},
want: result{
mime: "",
data: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mime, data := ParseDataUri(tt.args.uri)
got := result{
mime: mime,
data: data,
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParseDataUri() = %v, want %v", got, tt.want)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui' as ui;
import 'dart:ui';

import 'package:app_links/app_links.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:get/get.dart';

Expand All @@ -10,6 +13,7 @@ class CreateController extends GetxController
final isResolving = false.obs;
final showAdvanced = false.obs;
late TabController advancedTabController;
final fileDataUri = "".obs;

@override
void onInit() {
Expand All @@ -22,4 +26,13 @@ class CreateController extends GetxController
advancedTabController.dispose();
super.onClose();
}

void setFileDataUri(Uint8List bytes) {
fileDataUri.value =
"data:application/x-bittorrent;base64,${base64.encode(bytes)}";
}

void clearFileDataUri() {
fileDataUri.value = "";
}
}
Loading

0 comments on commit a5d8713

Please sign in to comment.