feature: support cloudflare r2

pull/3671/head
chalkim 2025-01-15 09:59:41 +08:00
parent 2ef8f78f2d
commit 75c5bd14c1
14 changed files with 286 additions and 10 deletions

View File

@ -13,6 +13,7 @@ export async function makeTorrent(
name: string,
pieceLen: number,
privateFlag: boolean,
r2Flag: boolean,
source: string,
webSeeds: string[]
) {
@ -29,6 +30,7 @@ export async function makeTorrent(
name: name,
pieceLen: pieceLen,
private: privateFlag,
r2: r2Flag,
source: source,
webSeeds: webSeeds,
});

View File

@ -81,6 +81,11 @@
<input type="checkbox" v-model="privateFlag" tabindex="7" />
{{ $t("prompts.privateTorrent") }}
</p>
<div>
<input type="checkbox" v-model="r2Flag" tabindex="8"/>
{{ $t("prompts.r2") }}
</div>
</div>
<div class="card-action">
@ -113,6 +118,7 @@ export default {
name: "",
pieceLen: 0,
privateFlag: false,
r2Flag: false,
source: "",
webSeeds: [],
detailedView: false
@ -148,6 +154,7 @@ export default {
this.name = res.name;
this.pieceLen = res.pieceLen;
this.privateFlag = res.private;
this.r2Flag = res.r2Flag;
this.source = res.source;
this.webSeeds = res.webSeeds.join("\n");
});
@ -175,6 +182,7 @@ export default {
this.name,
parseInt(this.pieceLen),
this.privateFlag,
this.r2Flag,
this.source,
this.webSeeds.split("\n").map((t) => t.trim()).filter((t) => t),
).then(
@ -198,6 +206,7 @@ export default {
this.date = true;
this.pieceLen = 18;
this.privateFlag = false;
this.r2Flag=false;
this.source = "";
this.webSeeds = [];
} catch (e) {

View File

@ -154,7 +154,8 @@
"source": "源:",
"includeDate": "写入创建日期",
"privateTorrent": "私有 torrent 不会在DHT网络上分发",
"publishMessage": "你确定要发布这个种子吗?"
"publishMessage": "你确定要发布这个种子吗?",
"r2": "上传到 Cloudflare R2 进行辅种"
},
"search": {
"images": "图像",
@ -260,7 +261,15 @@
"qbSettingsDescription": "你可以在此设置 qBittorrent WebUI 的地址、用户名和密码。",
"qbUrl": "qBittorrent WebUI 地址",
"qbUsername": "qBittorrent WebUI 用户名",
"qbPassword": "qBittorrent WebUI 密码"
"qbPassword": "qBittorrent WebUI 密码",
"r2": "辅种 (Cloudflare R2)",
"r2Description": "你可以在此设置 Cloudflare R2 的API密钥。",
"accountKeySecretHidden": "API 密钥已隐藏",
"accountId": "账户 ID",
"accountKeyId": "访问密钥 ID",
"accountKeySecret": "机密访问密钥",
"bucket": "存储桶名称",
"domain": "公开访问域"
},
"sidebar": {
"help": "帮助",

View File

@ -55,6 +55,11 @@ interface SettingsTorrent {
qbUrl?: string;
qbUsername?: string;
qbPassword?: string;
accountId?: string;
accountKeyId?: string;
accountKeySecret?: string;
bucket?: string;
domain?: string;
}
interface SettingsUnit {

View File

@ -99,6 +99,19 @@
<span v-if="fileStore.selectedCount > 0"
>{{ fileStore.selectedCount }} selected</span
>
<action
v-if="isTorrent"
icon="publish"
:label="t('buttons.publish')"
show="publish"
/>
<action
v-else-if="headerButtons.torrent"
icon="app_registration"
:label="t('buttons.torrent')"
show="torrent"
/>
<action
v-if="headerButtons.share"
icon="share"

View File

@ -275,6 +275,59 @@
id="qbPassword"
></input>
</p>
<h3>{{ t("settings.r2") }}</h3>
<p class="small">{{ t("settings.r2Description") }}</p>
<p>
<label for="accountId">{{ t("settings.accountId") }}</label>
<input
class="input input--block"
type="text"
v-model="settings.torrent.accountId"
id="accountId"
/>
</p>
<p>
<label for="accountKeyId">{{ t("settings.accountKeyId") }}</label>
<input
class="input input--block"
type="text"
v-model="settings.torrent.accountKeyId"
id="accountKeyId"
/>
</p>
<p>
<label for="accountKeySecret">{{ t("settings.accountKeySecret") }}</label>
<input
class="input input--block"
type="password"
:placeholder="t('settings.accountKeySecretHidden')"
v-model="settings.torrent.accountKeySecret"
id="accountKeySecret"
/>
</p>
<p>
<label for="bucket">{{ t("settings.bucket") }}</label>
<input
class="input input--block"
type="text"
v-model="settings.torrent.bucket"
id="bucket"
/>
</p>
<p>
<label for="domain">{{ t("settings.domain") }}</label>
<input
class="input input--block"
type="text"
v-model="settings.torrent.domain"
id="bucket"
/>
</p>
</div>
<div class="card-action">
@ -384,6 +437,14 @@ const save = async () => {
}
newSettings.shell = shellValue.value.split("\n");
// remove qbPassword & accountKeySecret if empty
if (newSettings.torrent.qbPassword === "") {
delete newSettings.torrent.qbPassword;
}
if (newSettings.torrent.accountKeySecret === "") {
delete newSettings.torrent.accountKeySecret;
}
if (newSettings.branding.theme !== getTheme()) {
setTheme(newSettings.branding.theme);
}

19
go.mod
View File

@ -54,6 +54,24 @@ require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/asticode/go-astikit v0.42.0 // indirect
github.com/asticode/go-astits v1.13.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.32.8 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
github.com/aws/aws-sdk-go-v2/config v1.28.10 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.51 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.27 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.8 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/benbjohnson/immutable v0.3.0 // indirect
github.com/bits-and-blooms/bitset v1.2.2 // indirect
@ -79,6 +97,7 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect

40
go.sum
View File

@ -92,6 +92,42 @@ github.com/asticode/go-astisub v0.26.2/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2z
github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
github.com/asticode/go-astits v1.13.0 h1:XOgkaadfZODnyZRR5Y0/DWkA9vrkLLPLeeOvDwfKZ1c=
github.com/asticode/go-astits v1.13.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
github.com/aws/aws-sdk-go-v2 v1.32.8 h1:cZV+NUS/eGxKXMtmyhtYPJ7Z4YLoI/V8bkTdRZfYhGo=
github.com/aws/aws-sdk-go-v2 v1.32.8/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
github.com/aws/aws-sdk-go-v2/config v1.28.10 h1:fKODZHfqQu06pCzR69KJ3GuttraRJkhlC8g80RZ0Dfg=
github.com/aws/aws-sdk-go-v2/config v1.28.10/go.mod h1:PvdxRYZ5Um9QMq9PQ0zHHNdtKK+he2NHtFCUFMXWXeg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.51 h1:F/9Sm6Y6k4LqDesZDPJCLxQGXNNHd/ZtJiWd0lCZKRk=
github.com/aws/aws-sdk-go-v2/credentials v1.17.51/go.mod h1:TKbzCHm43AoPyA+iLGGcruXd4AFhF8tOmLex2R9jWNQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 h1:IBAoD/1d8A8/1aA8g4MBVtTRHhXRiNAgwdbo/xRM2DI=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23/go.mod h1:vfENuCM7dofkgKpYzuzf1VT1UKkA/YL3qanfBn7HCaA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 h1:jSJjSBzw8VDIbWv+mmvBSP8ezsztMYJGH+eKqi9AmNs=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27/go.mod h1:/DAhLbFRgwhmvJdOfSm+WwikZrCuUJiA4WgJG0fTNSw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 h1:l+X4K77Dui85pIj5foXDhPlnqcNRG2QUyvca300lXh8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27/go.mod h1:KvZXSFEXm6x84yE8qffKvT3x8J5clWnVFXphpohhzJ8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.27 h1:AmB5QxnD+fBFrg9LcqzkgF/CaYvMyU/BTlejG4t1S7Q=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.27/go.mod h1:Sai7P3xTiyv9ZUYO3IFxMnmiIP759/67iQbU4kdmkyU=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.8 h1:iwYS40JnrBeA9e9aI5S6KKN4EB2zR4iUVYN0nwVivz4=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.8/go.mod h1:Fm9Mi+ApqmFiknZtGpohVcBGvpTu542VC4XO9YudRi0=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 h1:cWno7lefSH6Pp+mSznagKCgfDGeZRin66UvYUqAkyeA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8/go.mod h1:tPD+VjU3ABTBoEJ3nctu5Nyg4P4yjqSH5bJGGkY4+XE=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.8 h1:/Mn7gTedG86nbpjT4QEKsN1D/fThiYe1qvq7WsBGNHg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.8/go.mod h1:Ae3va9LPmvjj231ukHB6UeT8nS7wTPfC3tMZSZMwNYg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.2 h1:a7aQ3RW+ug4IbhoQp29NZdc7vqrzKZZfWZSaQAXOZvQ=
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.2/go.mod h1:xMekrnhmJ5aqmyxtmALs7mlvXw5xRh+eYjOjvrIIFJ4=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 h1:YqtxripbjWb2QLyzRK9pByfEDvgg95gpC2AyDq4hFE8=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.9/go.mod h1:lV8iQpg6OLOfBnqbGMBKYjilBlf633qwHnBEiMSPoHY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 h1:6dBT1Lz8fK11m22R+AqfRsFn8320K0T5DTGxxOQBSMw=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8/go.mod h1:/kiBvRQXBc6xeJTYzhSdGvJ5vm1tjaDEjH+MSeRJnlY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 h1:VwhTrsTuVn52an4mXx29PqRzs2Dvu921NpGk7y43tAM=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.6/go.mod h1:+8h7PZb3yY5ftmVLD7ocEoE98hdc8PoKS0H3wfx1dlc=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
@ -259,6 +295,9 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@ -708,6 +747,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@ -35,7 +35,9 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
Torrent: d.settings.Torrent,
}
// Hide sensitive information.
data.Torrent.QbPassword = ""
data.Torrent.AccountKeySecret = ""
return renderJSON(w, r, data)
})

View File

@ -43,7 +43,6 @@ var torrentPostHandler = withPermTorrent(func(w http.ResponseWriter, r *http.Req
if err != nil {
return errToStatus(err), err
}
fPath := file.RealPath()
var body users.CreateTorrentBody
if r.Body != nil {
@ -53,7 +52,7 @@ var torrentPostHandler = withPermTorrent(func(w http.ResponseWriter, r *http.Req
defer r.Body.Close()
}
err = d.Torrent.MakeTorrent(fPath, body)
err = d.Torrent.MakeTorrent(file, body)
if err != nil {
return http.StatusInternalServerError, err
}

View File

@ -1,8 +1,13 @@
package settings
type Torrent struct {
TrackersListUrl string `json:"trackersListUrl"`
QbUrl string `json:"qbUrl"`
QbUsername string `json:"qbUsername"`
QbPassword string `json:"qbPassword"`
TrackersListUrl string `json:"trackersListUrl"`
QbUrl string `json:"qbUrl"`
QbUsername string `json:"qbUsername"`
QbPassword string `json:"qbPassword"`
AccountId string `json:"accountId"`
AccountKeyId string `json:"accountKeyId"`
AccountKeySecret string `json:"accountKeySecret"`
Bucket string `json:"bucket"`
Domain string `json:"domain"`
}

80
torrent/r2_client.go Normal file
View File

@ -0,0 +1,80 @@
package torrent
import (
"context"
"fmt"
"log"
"os"
"sync"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
)
var (
client *s3.Client
once sync.Once
)
// InitializeClient initializes the S3 client and ensures it's only done once.
func InitializeClient(accountId, accessKeyId, accessKeySecret string) {
once.Do(func() {
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyId, accessKeySecret, "")),
config.WithRegion("auto"),
)
if err != nil {
log.Fatalf("failed to load config: %v", err)
}
client = s3.NewFromConfig(cfg, func(o *s3.Options) {
o.BaseEndpoint = aws.String(fmt.Sprintf("https://%s.r2.cloudflarestorage.com", accountId))
})
})
}
// GetClient returns the initialized S3 client.
func GetClient() *s3.Client {
if client == nil {
log.Fatal("S3 client is not initialized. Call InitializeClient first.")
}
return client
}
// ListFiles lists all files in the specified bucket.
func listFiles(bucketName string) ([]types.Object, error) {
client := GetClient()
output, err := client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{
Bucket: &bucketName,
})
if err != nil {
return nil, fmt.Errorf("failed to list files: %w", err)
}
return output.Contents, nil
}
// UploadFile uploads a file to the specified bucket.
func uploadHandler(bucketName, key, filePath string, c chan string) error { //nolint:interfacer
client := GetClient()
// Open the file
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
// Upload the file
_, err = client.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: &bucketName,
Key: &key,
Body: file,
})
if err != nil {
return fmt.Errorf("failed to upload file: %w", err)
}
return nil
}

View File

@ -7,6 +7,7 @@ import (
"os/exec"
"strings"
"github.com/filebrowser/filebrowser/v2/files"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users"
)
@ -16,7 +17,8 @@ type Torrent struct {
*users.User
}
func (t *Torrent) MakeTorrent(fPath string, body users.CreateTorrentBody) error {
func (t *Torrent) MakeTorrent(file *files.FileInfo, body users.CreateTorrentBody) error {
fPath := file.RealPath()
tPath := fPath + ".torrent"
// 设置 mktorrent 命令的选项
@ -33,8 +35,37 @@ func (t *Torrent) MakeTorrent(fPath string, body users.CreateTorrentBody) error
WebSeeds: body.WebSeeds,
}
args := buildArgs(opts)
// 上传到 R2
if body.R2 {
// var bucketName = "moezakura"
// var accountId = "6a59886e546396fc9076ec50764dc9f3"
// var accessKeyId = "d65f9e3347d046e0583a8d846aa8cb46"
// var accessKeySecret = "a8a57f4d1525eada05f7e553815a89c4da0a3b28745eaddea89e4c52334f056e"
var accountId = t.Settings.Torrent.AccountId
var accessKeyId = t.Settings.Torrent.AccountKeyId
var accessKeySecret = t.Settings.Torrent.AccountKeySecret
var bucketName = t.Settings.Torrent.Bucket
// will only be called once
InitializeClient(accountId, accessKeyId, accessKeySecret)
// remove first slash
var key = file.Path[1:]
sigc := make(chan string)
go uploadHandler(bucketName, key, fPath, sigc)
// result := <-sigc
// fmt.Println(result)
// join domain and key to url
// eg: domain: download.moezakura.click, key: test.txt
// url: https://download.moezakura.click/test.txt
var url = "https://" + t.Torrent.Domain + "/" + key
opts.WebSeeds = append(opts.WebSeeds, url)
}
// 调用 mktorrent 命令
args := buildArgs(opts)
cmd := exec.Command("mktorrent", args...)
err := cmd.Run()

View File

@ -7,6 +7,7 @@ type CreateTorrentBody struct {
Name string `json:"name"`
PieceLen int `json:"pieceLen"`
Private bool `json:"private"`
R2 bool `json:"r2"`
Source string `json:"source"`
WebSeeds []string `json:"webSeeds"`
}