up
Some checks failed
docker / build-docker (push) Failing after 56s

This commit is contained in:
royalcat 2025-06-08 06:18:23 +04:00
parent 84fc019e46
commit 3c0ba3cc9f
17 changed files with 1965 additions and 1880 deletions

View file

@ -5,7 +5,7 @@ go 1.24.1
require (
git.kmsign.ru/royalcat/tstor/plugins/archive v0.0.0-20250420233812-dbf843ad072e
git.kmsign.ru/royalcat/tstor/plugins/qbittorrent v0.0.0-20250420233812-dbf843ad072e
git.kmsign.ru/royalcat/tstor/server v0.0.0-20250420233812-dbf843ad072e
git.kmsign.ru/royalcat/tstor/server v0.0.0-20250606103346-f48093ad3251
)
require (
@ -46,6 +46,7 @@ require (
github.com/grafana/otel-profiling-go v0.5.1 // indirect
github.com/grafana/pyroscope-go v1.2.2 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
@ -60,9 +61,6 @@ require (
github.com/knadh/koanf/providers/file v1.2.0 // indirect
github.com/knadh/koanf/providers/structs v1.0.0 // indirect
github.com/knadh/koanf/v2 v2.2.0 // indirect
github.com/labstack/echo-contrib v0.17.3 // indirect
github.com/labstack/echo/v4 v4.13.3 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
@ -94,8 +92,6 @@ require (
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/urfave/cli/v2 v2.27.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vektah/gqlparser/v2 v2.5.25 // indirect
github.com/viccon/sturdyc v1.1.5 // indirect
github.com/willscott/go-nfs-client v0.0.0-20240104095149-b44639837b00 // indirect
@ -123,7 +119,6 @@ require (
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/grpc v1.72.0 // indirect

View file

@ -21,8 +21,8 @@ git.kmsign.ru/royalcat/tstor/plugins/archive v0.0.0-20250420233812-dbf843ad072e
git.kmsign.ru/royalcat/tstor/plugins/archive v0.0.0-20250420233812-dbf843ad072e/go.mod h1:b7qGAuR7TPQiH5c84sC0CSWHFC2XApvNmzuqabrB/AA=
git.kmsign.ru/royalcat/tstor/plugins/qbittorrent v0.0.0-20250420233812-dbf843ad072e h1:7al9sFgxJglONyGMkeduulRM3C1NwwuoRGqXiTUaJvk=
git.kmsign.ru/royalcat/tstor/plugins/qbittorrent v0.0.0-20250420233812-dbf843ad072e/go.mod h1:3P9WgIZhDzbsuAPF/udAyFAWSooIvE81UGvDepPZMZY=
git.kmsign.ru/royalcat/tstor/server v0.0.0-20250420233812-dbf843ad072e h1:pydEjTn3y7aUFW8Je9YrxJzbGmcYbMdu1kkQZL1lBf4=
git.kmsign.ru/royalcat/tstor/server v0.0.0-20250420233812-dbf843ad072e/go.mod h1:kQyKUtvpB30J+DMeaDXHfJAxdkVUbqMh7ayF72XtJ2w=
git.kmsign.ru/royalcat/tstor/server v0.0.0-20250606103346-f48093ad3251 h1:cKjrmPg7OXQwUUvRC9jF2VizAv+1M3UVsVGmCE4KY8A=
git.kmsign.ru/royalcat/tstor/server v0.0.0-20250606103346-f48093ad3251/go.mod h1:MmZULZds+oT+gIFUNi+kG2VfW3uRMEsP2OqZ1TXpzc4=
github.com/99designs/gqlgen v0.17.72 h1:2JDAuutIYtAN26BAtigfLZFnTN53fpYbIENL8bVgAKY=
github.com/99designs/gqlgen v0.17.72/go.mod h1:BoL4C3j9W2f95JeWMrSArdDNGWmZB9MOS2EMHJDZmUc=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -39,6 +39,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anacrolix/dht/v2 v2.22.1 h1:mgsljPXyA/EWA7uUDSNjx7wf6gsfhppjVIp9auVeR6w=
github.com/anacrolix/dht/v2 v2.22.1/go.mod h1:seXRz6HLw8zEnxlysf9ye2eQbrKUmch6PyOHpe/Nb/U=
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
@ -224,6 +225,7 @@ github.com/grafana/pyroscope-go v1.2.2 h1:uvKCyZMD724RkaCEMrSTC38Yn7AnFe8S2wiAIY
github.com/grafana/pyroscope-go v1.2.2/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -279,12 +281,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo-contrib v0.17.3 h1:hj+qXksKZG1scSe9ksUXMtv7fZYN+PtQT+bPcYA3/TY=
github.com/labstack/echo-contrib v0.17.3/go.mod h1:TcRBrzW8jcC4JD+5Dc/pvOyAps0rtgzj7oBqoR3nYsc=
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
@ -423,10 +419,6 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser/v2 v2.5.25 h1:FmWtFEa+invTIzWlWK6Vk7BVEZU/97QBzeI8Z1JjGt8=
github.com/vektah/gqlparser/v2 v2.5.25/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
github.com/viccon/sturdyc v1.1.5 h1:GLQDnsyKt3L/tpdWCIARIRefn+5DAyvqu+0irBwt+vk=
@ -605,8 +597,6 @@ golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -676,6 +666,7 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,7 @@ func (ArchivePlugin) Name() string {
var _ tstor.DaemonPlugin = (*ArchivePlugin)(nil)
// NewDaemon implements tstor.DaemonPlugin.
func (a *ArchivePlugin) NewDaemon(context.Context, *koanf.Koanf) (daemon.Daemon, error) {
func (a *ArchivePlugin) NewDaemon(context.Context, *koanf.Koanf, vfs.Filesystem) (daemon.Daemon, error) {
return &Daemon{}, nil
}

View file

@ -1,5 +1,6 @@
generate-gprc: proto/*.proto
protoc -I=../../ -I=proto \
--go_out=src/delivery/grpc/pb --go_opt=paths=source_relative \
--go-grpc_out=src/delivery/grpc/pb --go-grpc_opt=paths=source_relative,require_unimplemented_servers=false \
$<
mkdir -p src/delivery/grpc && \
protoc -I=../../ -I=. \
--go_out=src/delivery/grpc --go_opt=paths=source_relative \
--go-grpc_out=src/delivery/grpc --go-grpc_opt=paths=source_relative,require_unimplemented_servers=false \
$^

View file

@ -24,26 +24,30 @@ var _ proto.QBitTorrentServiceServer = (*grpcService)(nil)
// Cleanup implements proto.QBitTorrentServiceServer.
func (g *grpcService) Cleanup(ctx context.Context, req *proto.CleanupRequest) (*proto.CleanupResponse, error) {
hashes, err := g.d.Cleanup(ctx, req.Act)
hashes, err := g.d.Cleanup(ctx, req.GetAct())
if err != nil {
return nil, err
}
return &proto.CleanupResponse{
Count: int32(len(hashes)),
Hashes: hashes,
}, nil
resp := proto.CleanupResponse{}
resp.SetCount(int32(len(hashes)))
resp.SetHashes(hashes)
return &resp, nil
}
// CleanupUnregistred implements proto.QBitTorrentServiceServer.
func (g *grpcService) CleanupUnregistred(ctx context.Context, req *proto.CleanupUnregistredRequest) (*proto.CleanupUnregistredResponse, error) {
hashes, err := g.d.CleanupUnregistred(ctx, req.Act)
hashes, err := g.d.CleanupUnregistred(ctx, req.GetAct())
if err != nil {
return nil, err
}
return &proto.CleanupUnregistredResponse{
Count: int32(len(hashes)),
Hashes: hashes,
}, nil
resp := proto.CleanupUnregistredResponse{}
resp.SetCount(int32(len(hashes)))
resp.SetHashes(hashes)
return &resp, nil
}
// GetTorrents implements proto.QBitTorrentServiceServer.

View file

@ -8,6 +8,7 @@ import (
"os"
"path"
"slices"
"strings"
"git.kmsign.ru/royalcat/tstor/plugins/qbittorrent/pkg/qbittorrent"
"git.kmsign.ru/royalcat/tstor/server/pkg/rlog"
@ -69,6 +70,38 @@ func (d *QBittorrentDaemon) Cleanup(ctx context.Context, run bool) ([]string, er
func (d *QBittorrentDaemon) CleanupUnregistred(ctx context.Context, run bool) ([]string, error) {
d.log.Info(ctx, "cleanup started")
err := d.sourceFilesKV.Range(ctx, func(sourcePath, hash string) error {
log := d.log.With(slog.String("sourcePath", sourcePath))
if !strings.HasSuffix(sourcePath, ".torrent") {
log.Warn(ctx, "skipping non-torrent file", slog.String("path", sourcePath))
return nil
}
if d.registeredTorrents.Contains(hash) {
log.Debug(ctx, "torrent already registered, skipping", slog.String("infohash", hash))
return nil
}
f, err := d.sourceFS.Open(ctx, sourcePath)
if err != nil {
log.Error(ctx, "failed to open source file", slog.String("path", sourcePath), rlog.Error(err))
return fmt.Errorf("failed to open source file: %w", err)
}
_, err = d.GetFS(ctx, sourcePath, f)
if err != nil {
log.Error(ctx, "failed to get filesystem for source file", slog.String("path", sourcePath), rlog.Error(err))
return fmt.Errorf("failed to get filesystem for source file: %w", err)
}
return nil
})
if err != nil {
d.log.Error(ctx, "failed to iterate source files", rlog.Error(err))
return nil, fmt.Errorf("failed to iterate source files: %w", err)
}
torrentInfos, err := d.client.qb.Torrent().GetTorrents(ctx, &qbittorrent.TorrentOption{})
if err != nil {
d.log.Error(ctx, "failed to get torrents", rlog.Error(err))

View file

@ -9,7 +9,6 @@ import (
"os"
"path"
"path/filepath"
"sync"
"time"
"git.kmsign.ru/royalcat/tstor/plugins/qbittorrent/pkg/qbittorrent"
@ -21,22 +20,28 @@ import (
"github.com/anacrolix/torrent/types/infohash"
infohash_v2 "github.com/anacrolix/torrent/types/infohash-v2"
mapset "github.com/deckarep/golang-set/v2"
"github.com/dgraph-io/badger/v4"
"github.com/knadh/koanf/v2"
"github.com/royalcat/ctxio"
"github.com/royalcat/kv"
"github.com/royalcat/kv/kvbadger"
"go.opentelemetry.io/otel"
)
var trace = otel.Tracer("git.kmsign.ru/royalcat/tstor/plugins/qbittorrent/daemon")
type QBittorrentDaemon struct {
sourceFS vfs.Filesystem
proc *os.Process
qb qbittorrent.Client
client *cacheClient
sourceFilesMu sync.Mutex
sourceFiles map[string]string // [sourcePath]infohash
// sourceFilesMu sync.Mutex
// sourceFiles map[string]string // [sourcePath]infohash
registeredTorrents mapset.Set[string] // infohash list
sourceFilesKV kv.Store[string, string] // [sourcePath]infohash
registeredTorrents mapset.Set[string] // infohash list
dataDir string
log *rlog.Logger
@ -53,7 +58,7 @@ WebUI\Password_PBKDF2="@ByteArray(qef5I4wZBkDG+PP6/5mQwA==:LoTmorQM/QM5RHI4+dOiu
const daemonName = "qbittorrent"
func New(ctx context.Context, koanf *koanf.Koanf) (daemon.Daemon, error) {
func New(ctx context.Context, koanf *koanf.Koanf, sourceFS vfs.Filesystem) (daemon.Daemon, error) {
log := rlog.Component(daemonName)
log.Debug(ctx, "QBittorrent plugin loaded. Starting qbittorrent-nox")
@ -128,11 +133,19 @@ func New(ctx context.Context, koanf *koanf.Koanf) (daemon.Daemon, error) {
return nil, err
}
sourceFilesKV, err := kvbadger.NewRaw[string, string](kvbadger.Options[string]{
BadgerOptions: badger.DefaultOptions(path.Join(config.MetadataDir, "tstor", "source_files")),
})
if err != nil {
return nil, err
}
return &QBittorrentDaemon{
sourceFS: sourceFS,
qb: qb,
proc: proc,
dataDir: config.DataDir,
sourceFiles: make(map[string]string),
sourceFilesKV: sourceFilesKV,
registeredTorrents: mapset.NewSet[string](),
client: wrapClient(qb),
log: rlog.Component(daemonName),
@ -165,7 +178,7 @@ func torrentDataPath(dataDir string, ih string) (string, error) {
return filepath.Abs(path.Join(dataDir, ih))
}
func (fs *QBittorrentDaemon) GetFS(ctx context.Context, sourcePath string, file vfs.File) (vfs.Filesystem, error) {
func (d *QBittorrentDaemon) GetFS(ctx context.Context, sourcePath string, file vfs.File) (vfs.Filesystem, error) {
ctx, span := trace.Start(ctx, "GetTorrentFS")
defer span.End()
@ -174,7 +187,7 @@ func (fs *QBittorrentDaemon) GetFS(ctx context.Context, sourcePath string, file
return nil, err
}
log := fs.log.With(slog.String("file", file.Name()))
log := d.log.With(slog.String("file", file.Name()))
ih, err := readInfoHash(ctx, file)
if err != nil {
@ -182,7 +195,7 @@ func (fs *QBittorrentDaemon) GetFS(ctx context.Context, sourcePath string, file
}
log = log.With(slog.String("infohash", ih.HexString()))
torrentPath, err := torrentDataPath(fs.dataDir, ih.HexString())
torrentPath, err := torrentDataPath(d.dataDir, ih.HexString())
if err != nil {
return nil, fmt.Errorf("error getting torrent path: %w", err)
}
@ -190,16 +203,17 @@ func (fs *QBittorrentDaemon) GetFS(ctx context.Context, sourcePath string, file
log.Debug(ctx, "creating fs for torrent")
err = fs.syncTorrentState(ctx, file, ih, torrentPath)
err = d.syncTorrentState(ctx, file, ih, torrentPath)
if err != nil {
return nil, fmt.Errorf("error syncing torrent state: %w", err)
}
fs.sourceFilesMu.Lock()
fs.sourceFiles[sourcePath] = ih.HexString()
fs.sourceFilesMu.Unlock()
err = d.sourceFilesKV.Set(ctx, sourcePath, ih.HexString())
if err != nil {
log.Error(ctx, "error setting source file in kv store", rlog.Error(err))
}
return newTorrentFS(ctx, fs.client, file.Name(), ih.HexString(), stat.ModTime(), torrentPath)
return newTorrentFS(ctx, d.client, file.Name(), ih.HexString(), stat.ModTime(), torrentPath)
}
func (d *QBittorrentDaemon) syncTorrentState(ctx context.Context, file vfs.File, ih metainfo.Hash, torrentPath string) error {

View file

@ -10,9 +10,10 @@ toolchain go1.24.2
exclude github.com/envoyproxy/go-control-plane/envoy v1.32.3
require (
git.kmsign.ru/royalcat/tstor/server v0.0.0-20250420233812-dbf843ad072e
git.kmsign.ru/royalcat/tstor/server v0.0.0-20250607150550-84fc019e46fe
github.com/anacrolix/torrent v1.58.1
github.com/deckarep/golang-set/v2 v2.8.0
github.com/dgraph-io/badger/v4 v4.7.0
github.com/google/go-github/v63 v63.0.0
github.com/gorilla/schema v1.4.1
github.com/hashicorp/golang-lru/v2 v2.0.7
@ -20,6 +21,8 @@ require (
github.com/knadh/koanf/v2 v2.2.0
github.com/royalcat/btrgo v0.0.0-20240318160410-19bd27154450
github.com/royalcat/ctxio v0.0.0-20240602084623-009bd79b3176
github.com/royalcat/kv v0.0.0-20240723215915-954e36a2491d
github.com/royalcat/kv/kvbadger v0.0.0-20240723215915-954e36a2491d
github.com/stretchr/testify v1.10.0
github.com/viccon/sturdyc v1.1.5
go.opentelemetry.io/otel v1.35.0
@ -30,8 +33,6 @@ require (
google.golang.org/protobuf v1.36.6
)
replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20250324211829-b45e905df463
require (
github.com/99designs/gqlgen v0.17.72 // indirect
github.com/agnivade/levenshtein v1.2.1 // indirect
@ -46,7 +47,6 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgraph-io/badger/v4 v4.7.0 // indirect
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/structs v1.1.0 // indirect
@ -63,6 +63,7 @@ require (
github.com/grafana/otel-profiling-go v0.5.1 // indirect
github.com/grafana/pyroscope-go v1.2.2 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
@ -71,9 +72,6 @@ require (
github.com/knadh/koanf/parsers/yaml v1.0.0 // indirect
github.com/knadh/koanf/providers/env v1.1.0 // indirect
github.com/knadh/koanf/providers/file v1.2.0 // indirect
github.com/labstack/echo-contrib v0.17.3 // indirect
github.com/labstack/echo/v4 v4.13.3 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
@ -90,8 +88,6 @@ require (
github.com/prometheus/procfs v0.16.1 // indirect
github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 // indirect
github.com/ravilushqa/otelgqlgen v0.17.0 // indirect
github.com/royalcat/kv v0.0.0-20240723215915-954e36a2491d // indirect
github.com/royalcat/kv/kvbadger v0.0.0-20240723215915-954e36a2491d // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/samber/lo v1.49.1 // indirect
@ -101,8 +97,6 @@ require (
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/urfave/cli/v2 v2.27.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vektah/gqlparser/v2 v2.5.25 // indirect
github.com/willscott/go-nfs-client v0.0.0-20240104095149-b44639837b00 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
@ -124,9 +118,11 @@ require (
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.4.0 // indirect
)
tool google.golang.org/grpc/cmd/protoc-gen-go-grpc

File diff suppressed because it is too large Load diff

View file

@ -576,7 +576,7 @@ func (c *client) DeleteTorrents(ctx context.Context, hashes []string, deleteFile
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("deleteFile", strconv.FormatBool(deleteFile))
formData.Add("deleteFiles", strconv.FormatBool(deleteFile))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/delete", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,

View file

@ -5,6 +5,7 @@ import (
qbdaemon "git.kmsign.ru/royalcat/tstor/plugins/qbittorrent/daemon"
"git.kmsign.ru/royalcat/tstor/server/src/daemon"
"git.kmsign.ru/royalcat/tstor/server/src/vfs"
"git.kmsign.ru/royalcat/tstor/server/tstor"
"github.com/knadh/koanf/v2"
)
@ -20,6 +21,6 @@ func (QBittorrentPlugin) Name() string {
var _ tstor.DaemonPlugin = (*QBittorrentPlugin)(nil)
func (QBittorrentPlugin) NewDaemon(ctx context.Context, koanf *koanf.Koanf) (daemon.Daemon, error) {
return qbdaemon.New(ctx, koanf)
func (QBittorrentPlugin) NewDaemon(ctx context.Context, koanf *koanf.Koanf, sfs vfs.Filesystem) (daemon.Daemon, error) {
return qbdaemon.New(ctx, koanf, sfs)
}

View file

@ -1,10 +1,13 @@
syntax = "proto3";
edition = "2023";
package qbittorrent;
package tstor.daemons.qbittorrent;
import "server/proto/filters/filters.proto";
import "google/protobuf/go_features.proto";
option features.(pb.go).api_level = API_OPAQUE;
option go_package = "github.com/royalcat/tstor/plugins/qbittorrent/proto";
option go_package = "git.kmsign.ru/royalcat/tstor/plugins/qbittorrent/delivery/grpc/proto";
import "proto/filters/filters.proto";
message Torrent {
string name = 1;
@ -23,13 +26,13 @@ service QBitTorrentService {
message GetTorrentsRequest { tstor.filters.IntFilter sources_count = 1; }
message GetTorrentsResponse { repeated Torrent torrents = 1; }
message CleanupRequest { bool act = 1; }
message CleanupRequest { bool act = 1 [ default = false ]; }
message CleanupResponse {
int32 count = 1;
repeated string hashes = 2;
}
message CleanupUnregistredRequest { bool act = 1; }
message CleanupUnregistredRequest { bool act = 1 [ default = false ]; }
message CleanupUnregistredResponse {
int32 count = 1;
repeated string hashes = 2;

View file

@ -2,16 +2,16 @@
// versions:
// protoc-gen-go v1.36.6
// protoc v6.30.2
// source: qbittorrent.proto
// source: proto/qbittorrent.proto
package proto
import (
filters "git.kmsign.ru/royalcat/tstor/server/src/delivery/grpc/pb/filters"
filters "git.kmsign.ru/royalcat/tstor/server/src/delivery/grpc/proto/filters"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
_ "google.golang.org/protobuf/types/gofeaturespb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
@ -23,17 +23,19 @@ const (
)
type Torrent struct {
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Hash string `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
SourceFiles []string `protobuf:"bytes,3,rep,name=source_files,json=sourceFiles,proto3" json:"source_files,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"opaque.v1"`
xxx_hidden_Name *string `protobuf:"bytes,1,opt,name=name"`
xxx_hidden_Hash *string `protobuf:"bytes,2,opt,name=hash"`
xxx_hidden_SourceFiles []string `protobuf:"bytes,3,rep,name=source_files,json=sourceFiles"`
XXX_raceDetectHookData protoimpl.RaceDetectHookData
XXX_presence [1]uint32
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Torrent) Reset() {
*x = Torrent{}
mi := &file_qbittorrent_proto_msgTypes[0]
mi := &file_proto_qbittorrent_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -45,7 +47,7 @@ func (x *Torrent) String() string {
func (*Torrent) ProtoMessage() {}
func (x *Torrent) ProtoReflect() protoreflect.Message {
mi := &file_qbittorrent_proto_msgTypes[0]
mi := &file_proto_qbittorrent_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -56,42 +58,105 @@ func (x *Torrent) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use Torrent.ProtoReflect.Descriptor instead.
func (*Torrent) Descriptor() ([]byte, []int) {
return file_qbittorrent_proto_rawDescGZIP(), []int{0}
}
func (x *Torrent) GetName() string {
if x != nil {
return x.Name
if x.xxx_hidden_Name != nil {
return *x.xxx_hidden_Name
}
return ""
}
return ""
}
func (x *Torrent) GetHash() string {
if x != nil {
return x.Hash
if x.xxx_hidden_Hash != nil {
return *x.xxx_hidden_Hash
}
return ""
}
return ""
}
func (x *Torrent) GetSourceFiles() []string {
if x != nil {
return x.SourceFiles
return x.xxx_hidden_SourceFiles
}
return nil
}
func (x *Torrent) SetName(v string) {
x.xxx_hidden_Name = &v
protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 3)
}
func (x *Torrent) SetHash(v string) {
x.xxx_hidden_Hash = &v
protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 3)
}
func (x *Torrent) SetSourceFiles(v []string) {
x.xxx_hidden_SourceFiles = v
}
func (x *Torrent) HasName() bool {
if x == nil {
return false
}
return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
}
func (x *Torrent) HasHash() bool {
if x == nil {
return false
}
return protoimpl.X.Present(&(x.XXX_presence[0]), 1)
}
func (x *Torrent) ClearName() {
protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
x.xxx_hidden_Name = nil
}
func (x *Torrent) ClearHash() {
protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 1)
x.xxx_hidden_Hash = nil
}
type Torrent_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
Name *string
Hash *string
SourceFiles []string
}
func (b0 Torrent_builder) Build() *Torrent {
m0 := &Torrent{}
b, x := &b0, m0
_, _ = b, x
if b.Name != nil {
protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 3)
x.xxx_hidden_Name = b.Name
}
if b.Hash != nil {
protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 3)
x.xxx_hidden_Hash = b.Hash
}
x.xxx_hidden_SourceFiles = b.SourceFiles
return m0
}
type GetTorrentsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
SourcesCount *filters.IntFilter `protobuf:"bytes,1,opt,name=sources_count,json=sourcesCount,proto3" json:"sources_count,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"opaque.v1"`
xxx_hidden_SourcesCount *filters.IntFilter `protobuf:"bytes,1,opt,name=sources_count,json=sourcesCount"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetTorrentsRequest) Reset() {
*x = GetTorrentsRequest{}
mi := &file_qbittorrent_proto_msgTypes[1]
mi := &file_proto_qbittorrent_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -103,7 +168,7 @@ func (x *GetTorrentsRequest) String() string {
func (*GetTorrentsRequest) ProtoMessage() {}
func (x *GetTorrentsRequest) ProtoReflect() protoreflect.Message {
mi := &file_qbittorrent_proto_msgTypes[1]
mi := &file_proto_qbittorrent_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -114,28 +179,52 @@ func (x *GetTorrentsRequest) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use GetTorrentsRequest.ProtoReflect.Descriptor instead.
func (*GetTorrentsRequest) Descriptor() ([]byte, []int) {
return file_qbittorrent_proto_rawDescGZIP(), []int{1}
}
func (x *GetTorrentsRequest) GetSourcesCount() *filters.IntFilter {
if x != nil {
return x.SourcesCount
return x.xxx_hidden_SourcesCount
}
return nil
}
func (x *GetTorrentsRequest) SetSourcesCount(v *filters.IntFilter) {
x.xxx_hidden_SourcesCount = v
}
func (x *GetTorrentsRequest) HasSourcesCount() bool {
if x == nil {
return false
}
return x.xxx_hidden_SourcesCount != nil
}
func (x *GetTorrentsRequest) ClearSourcesCount() {
x.xxx_hidden_SourcesCount = nil
}
type GetTorrentsRequest_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
SourcesCount *filters.IntFilter
}
func (b0 GetTorrentsRequest_builder) Build() *GetTorrentsRequest {
m0 := &GetTorrentsRequest{}
b, x := &b0, m0
_, _ = b, x
x.xxx_hidden_SourcesCount = b.SourcesCount
return m0
}
type GetTorrentsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Torrents []*Torrent `protobuf:"bytes,1,rep,name=torrents,proto3" json:"torrents,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"opaque.v1"`
xxx_hidden_Torrents *[]*Torrent `protobuf:"bytes,1,rep,name=torrents"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetTorrentsResponse) Reset() {
*x = GetTorrentsResponse{}
mi := &file_qbittorrent_proto_msgTypes[2]
mi := &file_proto_qbittorrent_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -147,7 +236,7 @@ func (x *GetTorrentsResponse) String() string {
func (*GetTorrentsResponse) ProtoMessage() {}
func (x *GetTorrentsResponse) ProtoReflect() protoreflect.Message {
mi := &file_qbittorrent_proto_msgTypes[2]
mi := &file_proto_qbittorrent_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -158,28 +247,50 @@ func (x *GetTorrentsResponse) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use GetTorrentsResponse.ProtoReflect.Descriptor instead.
func (*GetTorrentsResponse) Descriptor() ([]byte, []int) {
return file_qbittorrent_proto_rawDescGZIP(), []int{2}
}
func (x *GetTorrentsResponse) GetTorrents() []*Torrent {
if x != nil {
return x.Torrents
if x.xxx_hidden_Torrents != nil {
return *x.xxx_hidden_Torrents
}
}
return nil
}
type CleanupRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Act bool `protobuf:"varint,1,opt,name=act,proto3" json:"act,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
func (x *GetTorrentsResponse) SetTorrents(v []*Torrent) {
x.xxx_hidden_Torrents = &v
}
type GetTorrentsResponse_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
Torrents []*Torrent
}
func (b0 GetTorrentsResponse_builder) Build() *GetTorrentsResponse {
m0 := &GetTorrentsResponse{}
b, x := &b0, m0
_, _ = b, x
x.xxx_hidden_Torrents = &b.Torrents
return m0
}
type CleanupRequest struct {
state protoimpl.MessageState `protogen:"opaque.v1"`
xxx_hidden_Act bool `protobuf:"varint,1,opt,name=act,def=0"`
XXX_raceDetectHookData protoimpl.RaceDetectHookData
XXX_presence [1]uint32
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
// Default values for CleanupRequest fields.
const (
Default_CleanupRequest_Act = bool(false)
)
func (x *CleanupRequest) Reset() {
*x = CleanupRequest{}
mi := &file_qbittorrent_proto_msgTypes[3]
mi := &file_proto_qbittorrent_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -191,7 +302,7 @@ func (x *CleanupRequest) String() string {
func (*CleanupRequest) ProtoMessage() {}
func (x *CleanupRequest) ProtoReflect() protoreflect.Message {
mi := &file_qbittorrent_proto_msgTypes[3]
mi := &file_proto_qbittorrent_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -202,29 +313,61 @@ func (x *CleanupRequest) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use CleanupRequest.ProtoReflect.Descriptor instead.
func (*CleanupRequest) Descriptor() ([]byte, []int) {
return file_qbittorrent_proto_rawDescGZIP(), []int{3}
}
func (x *CleanupRequest) GetAct() bool {
if x != nil {
return x.Act
if protoimpl.X.Present(&(x.XXX_presence[0]), 0) {
return x.xxx_hidden_Act
}
}
return false
return Default_CleanupRequest_Act
}
func (x *CleanupRequest) SetAct(v bool) {
x.xxx_hidden_Act = v
protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
}
func (x *CleanupRequest) HasAct() bool {
if x == nil {
return false
}
return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
}
func (x *CleanupRequest) ClearAct() {
protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
}
type CleanupRequest_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
Act *bool
}
func (b0 CleanupRequest_builder) Build() *CleanupRequest {
m0 := &CleanupRequest{}
b, x := &b0, m0
_, _ = b, x
if b.Act != nil {
protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
x.xxx_hidden_Act = *b.Act
}
return m0
}
type CleanupResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Count int32 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
Hashes []string `protobuf:"bytes,2,rep,name=hashes,proto3" json:"hashes,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"opaque.v1"`
xxx_hidden_Count int32 `protobuf:"varint,1,opt,name=count"`
xxx_hidden_Hashes []string `protobuf:"bytes,2,rep,name=hashes"`
XXX_raceDetectHookData protoimpl.RaceDetectHookData
XXX_presence [1]uint32
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CleanupResponse) Reset() {
*x = CleanupResponse{}
mi := &file_qbittorrent_proto_msgTypes[4]
mi := &file_proto_qbittorrent_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -236,7 +379,7 @@ func (x *CleanupResponse) String() string {
func (*CleanupResponse) ProtoMessage() {}
func (x *CleanupResponse) ProtoReflect() protoreflect.Message {
mi := &file_qbittorrent_proto_msgTypes[4]
mi := &file_proto_qbittorrent_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -247,35 +390,77 @@ func (x *CleanupResponse) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use CleanupResponse.ProtoReflect.Descriptor instead.
func (*CleanupResponse) Descriptor() ([]byte, []int) {
return file_qbittorrent_proto_rawDescGZIP(), []int{4}
}
func (x *CleanupResponse) GetCount() int32 {
if x != nil {
return x.Count
return x.xxx_hidden_Count
}
return 0
}
func (x *CleanupResponse) GetHashes() []string {
if x != nil {
return x.Hashes
return x.xxx_hidden_Hashes
}
return nil
}
type CleanupUnregistredRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Act bool `protobuf:"varint,1,opt,name=act,proto3" json:"act,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
func (x *CleanupResponse) SetCount(v int32) {
x.xxx_hidden_Count = v
protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 2)
}
func (x *CleanupResponse) SetHashes(v []string) {
x.xxx_hidden_Hashes = v
}
func (x *CleanupResponse) HasCount() bool {
if x == nil {
return false
}
return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
}
func (x *CleanupResponse) ClearCount() {
protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
x.xxx_hidden_Count = 0
}
type CleanupResponse_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
Count *int32
Hashes []string
}
func (b0 CleanupResponse_builder) Build() *CleanupResponse {
m0 := &CleanupResponse{}
b, x := &b0, m0
_, _ = b, x
if b.Count != nil {
protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 2)
x.xxx_hidden_Count = *b.Count
}
x.xxx_hidden_Hashes = b.Hashes
return m0
}
type CleanupUnregistredRequest struct {
state protoimpl.MessageState `protogen:"opaque.v1"`
xxx_hidden_Act bool `protobuf:"varint,1,opt,name=act,def=0"`
XXX_raceDetectHookData protoimpl.RaceDetectHookData
XXX_presence [1]uint32
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
// Default values for CleanupUnregistredRequest fields.
const (
Default_CleanupUnregistredRequest_Act = bool(false)
)
func (x *CleanupUnregistredRequest) Reset() {
*x = CleanupUnregistredRequest{}
mi := &file_qbittorrent_proto_msgTypes[5]
mi := &file_proto_qbittorrent_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -287,7 +472,7 @@ func (x *CleanupUnregistredRequest) String() string {
func (*CleanupUnregistredRequest) ProtoMessage() {}
func (x *CleanupUnregistredRequest) ProtoReflect() protoreflect.Message {
mi := &file_qbittorrent_proto_msgTypes[5]
mi := &file_proto_qbittorrent_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -298,29 +483,61 @@ func (x *CleanupUnregistredRequest) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use CleanupUnregistredRequest.ProtoReflect.Descriptor instead.
func (*CleanupUnregistredRequest) Descriptor() ([]byte, []int) {
return file_qbittorrent_proto_rawDescGZIP(), []int{5}
}
func (x *CleanupUnregistredRequest) GetAct() bool {
if x != nil {
return x.Act
if protoimpl.X.Present(&(x.XXX_presence[0]), 0) {
return x.xxx_hidden_Act
}
}
return false
return Default_CleanupUnregistredRequest_Act
}
func (x *CleanupUnregistredRequest) SetAct(v bool) {
x.xxx_hidden_Act = v
protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
}
func (x *CleanupUnregistredRequest) HasAct() bool {
if x == nil {
return false
}
return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
}
func (x *CleanupUnregistredRequest) ClearAct() {
protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
}
type CleanupUnregistredRequest_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
Act *bool
}
func (b0 CleanupUnregistredRequest_builder) Build() *CleanupUnregistredRequest {
m0 := &CleanupUnregistredRequest{}
b, x := &b0, m0
_, _ = b, x
if b.Act != nil {
protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
x.xxx_hidden_Act = *b.Act
}
return m0
}
type CleanupUnregistredResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Count int32 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
Hashes []string `protobuf:"bytes,2,rep,name=hashes,proto3" json:"hashes,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"opaque.v1"`
xxx_hidden_Count int32 `protobuf:"varint,1,opt,name=count"`
xxx_hidden_Hashes []string `protobuf:"bytes,2,rep,name=hashes"`
XXX_raceDetectHookData protoimpl.RaceDetectHookData
XXX_presence [1]uint32
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CleanupUnregistredResponse) Reset() {
*x = CleanupUnregistredResponse{}
mi := &file_qbittorrent_proto_msgTypes[6]
mi := &file_proto_qbittorrent_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -332,7 +549,7 @@ func (x *CleanupUnregistredResponse) String() string {
func (*CleanupUnregistredResponse) ProtoMessage() {}
func (x *CleanupUnregistredResponse) ProtoReflect() protoreflect.Message {
mi := &file_qbittorrent_proto_msgTypes[6]
mi := &file_proto_qbittorrent_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -343,85 +560,108 @@ func (x *CleanupUnregistredResponse) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use CleanupUnregistredResponse.ProtoReflect.Descriptor instead.
func (*CleanupUnregistredResponse) Descriptor() ([]byte, []int) {
return file_qbittorrent_proto_rawDescGZIP(), []int{6}
}
func (x *CleanupUnregistredResponse) GetCount() int32 {
if x != nil {
return x.Count
return x.xxx_hidden_Count
}
return 0
}
func (x *CleanupUnregistredResponse) GetHashes() []string {
if x != nil {
return x.Hashes
return x.xxx_hidden_Hashes
}
return nil
}
var File_qbittorrent_proto protoreflect.FileDescriptor
func (x *CleanupUnregistredResponse) SetCount(v int32) {
x.xxx_hidden_Count = v
protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 2)
}
const file_qbittorrent_proto_rawDesc = "" +
func (x *CleanupUnregistredResponse) SetHashes(v []string) {
x.xxx_hidden_Hashes = v
}
func (x *CleanupUnregistredResponse) HasCount() bool {
if x == nil {
return false
}
return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
}
func (x *CleanupUnregistredResponse) ClearCount() {
protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
x.xxx_hidden_Count = 0
}
type CleanupUnregistredResponse_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
Count *int32
Hashes []string
}
func (b0 CleanupUnregistredResponse_builder) Build() *CleanupUnregistredResponse {
m0 := &CleanupUnregistredResponse{}
b, x := &b0, m0
_, _ = b, x
if b.Count != nil {
protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 2)
x.xxx_hidden_Count = *b.Count
}
x.xxx_hidden_Hashes = b.Hashes
return m0
}
var File_proto_qbittorrent_proto protoreflect.FileDescriptor
const file_proto_qbittorrent_proto_rawDesc = "" +
"\n" +
"\x11qbittorrent.proto\x12\vqbittorrent\x1a\"server/proto/filters/filters.proto\"T\n" +
"\x17proto/qbittorrent.proto\x12\x19tstor.daemons.qbittorrent\x1a!google/protobuf/go_features.proto\x1a\x1bproto/filters/filters.proto\"T\n" +
"\aTorrent\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" +
"\x04hash\x18\x02 \x01(\tR\x04hash\x12!\n" +
"\fsource_files\x18\x03 \x03(\tR\vsourceFiles\"S\n" +
"\x12GetTorrentsRequest\x12=\n" +
"\rsources_count\x18\x01 \x01(\v2\x18.tstor.filters.IntFilterR\fsourcesCount\"G\n" +
"\x13GetTorrentsResponse\x120\n" +
"\btorrents\x18\x01 \x03(\v2\x14.qbittorrent.TorrentR\btorrents\"\"\n" +
"\x0eCleanupRequest\x12\x10\n" +
"\x03act\x18\x01 \x01(\bR\x03act\"?\n" +
"\rsources_count\x18\x01 \x01(\v2\x18.tstor.filters.IntFilterR\fsourcesCount\"U\n" +
"\x13GetTorrentsResponse\x12>\n" +
"\btorrents\x18\x01 \x03(\v2\".tstor.daemons.qbittorrent.TorrentR\btorrents\")\n" +
"\x0eCleanupRequest\x12\x17\n" +
"\x03act\x18\x01 \x01(\b:\x05falseR\x03act\"?\n" +
"\x0fCleanupResponse\x12\x14\n" +
"\x05count\x18\x01 \x01(\x05R\x05count\x12\x16\n" +
"\x06hashes\x18\x02 \x03(\tR\x06hashes\"-\n" +
"\x19CleanupUnregistredRequest\x12\x10\n" +
"\x03act\x18\x01 \x01(\bR\x03act\"J\n" +
"\x06hashes\x18\x02 \x03(\tR\x06hashes\"4\n" +
"\x19CleanupUnregistredRequest\x12\x17\n" +
"\x03act\x18\x01 \x01(\b:\x05falseR\x03act\"J\n" +
"\x1aCleanupUnregistredResponse\x12\x14\n" +
"\x05count\x18\x01 \x01(\x05R\x05count\x12\x16\n" +
"\x06hashes\x18\x02 \x03(\tR\x06hashes2\x99\x02\n" +
"\x12QBitTorrentService\x12R\n" +
"\vGetTorrents\x12\x1f.qbittorrent.GetTorrentsRequest\x1a .qbittorrent.GetTorrentsResponse\"\x00\x12F\n" +
"\aCleanup\x12\x1b.qbittorrent.CleanupRequest\x1a\x1c.qbittorrent.CleanupResponse\"\x00\x12g\n" +
"\x12CleanupUnregistred\x12&.qbittorrent.CleanupUnregistredRequest\x1a'.qbittorrent.CleanupUnregistredResponse\"\x00B5Z3github.com/royalcat/tstor/plugins/qbittorrent/protob\x06proto3"
"\x06hashes\x18\x02 \x03(\tR\x06hashes2\xee\x02\n" +
"\x12QBitTorrentService\x12n\n" +
"\vGetTorrents\x12-.tstor.daemons.qbittorrent.GetTorrentsRequest\x1a..tstor.daemons.qbittorrent.GetTorrentsResponse\"\x00\x12b\n" +
"\aCleanup\x12).tstor.daemons.qbittorrent.CleanupRequest\x1a*.tstor.daemons.qbittorrent.CleanupResponse\"\x00\x12\x83\x01\n" +
"\x12CleanupUnregistred\x124.tstor.daemons.qbittorrent.CleanupUnregistredRequest\x1a5.tstor.daemons.qbittorrent.CleanupUnregistredResponse\"\x00BNZDgit.kmsign.ru/royalcat/tstor/plugins/qbittorrent/delivery/grpc/proto\x92\x03\x05\xd2>\x02\x10\x03b\beditionsp\xe8\a"
var (
file_qbittorrent_proto_rawDescOnce sync.Once
file_qbittorrent_proto_rawDescData []byte
)
func file_qbittorrent_proto_rawDescGZIP() []byte {
file_qbittorrent_proto_rawDescOnce.Do(func() {
file_qbittorrent_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_qbittorrent_proto_rawDesc), len(file_qbittorrent_proto_rawDesc)))
})
return file_qbittorrent_proto_rawDescData
}
var file_qbittorrent_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_qbittorrent_proto_goTypes = []any{
(*Torrent)(nil), // 0: qbittorrent.Torrent
(*GetTorrentsRequest)(nil), // 1: qbittorrent.GetTorrentsRequest
(*GetTorrentsResponse)(nil), // 2: qbittorrent.GetTorrentsResponse
(*CleanupRequest)(nil), // 3: qbittorrent.CleanupRequest
(*CleanupResponse)(nil), // 4: qbittorrent.CleanupResponse
(*CleanupUnregistredRequest)(nil), // 5: qbittorrent.CleanupUnregistredRequest
(*CleanupUnregistredResponse)(nil), // 6: qbittorrent.CleanupUnregistredResponse
var file_proto_qbittorrent_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_proto_qbittorrent_proto_goTypes = []any{
(*Torrent)(nil), // 0: tstor.daemons.qbittorrent.Torrent
(*GetTorrentsRequest)(nil), // 1: tstor.daemons.qbittorrent.GetTorrentsRequest
(*GetTorrentsResponse)(nil), // 2: tstor.daemons.qbittorrent.GetTorrentsResponse
(*CleanupRequest)(nil), // 3: tstor.daemons.qbittorrent.CleanupRequest
(*CleanupResponse)(nil), // 4: tstor.daemons.qbittorrent.CleanupResponse
(*CleanupUnregistredRequest)(nil), // 5: tstor.daemons.qbittorrent.CleanupUnregistredRequest
(*CleanupUnregistredResponse)(nil), // 6: tstor.daemons.qbittorrent.CleanupUnregistredResponse
(*filters.IntFilter)(nil), // 7: tstor.filters.IntFilter
}
var file_qbittorrent_proto_depIdxs = []int32{
7, // 0: qbittorrent.GetTorrentsRequest.sources_count:type_name -> tstor.filters.IntFilter
0, // 1: qbittorrent.GetTorrentsResponse.torrents:type_name -> qbittorrent.Torrent
1, // 2: qbittorrent.QBitTorrentService.GetTorrents:input_type -> qbittorrent.GetTorrentsRequest
3, // 3: qbittorrent.QBitTorrentService.Cleanup:input_type -> qbittorrent.CleanupRequest
5, // 4: qbittorrent.QBitTorrentService.CleanupUnregistred:input_type -> qbittorrent.CleanupUnregistredRequest
2, // 5: qbittorrent.QBitTorrentService.GetTorrents:output_type -> qbittorrent.GetTorrentsResponse
4, // 6: qbittorrent.QBitTorrentService.Cleanup:output_type -> qbittorrent.CleanupResponse
6, // 7: qbittorrent.QBitTorrentService.CleanupUnregistred:output_type -> qbittorrent.CleanupUnregistredResponse
var file_proto_qbittorrent_proto_depIdxs = []int32{
7, // 0: tstor.daemons.qbittorrent.GetTorrentsRequest.sources_count:type_name -> tstor.filters.IntFilter
0, // 1: tstor.daemons.qbittorrent.GetTorrentsResponse.torrents:type_name -> tstor.daemons.qbittorrent.Torrent
1, // 2: tstor.daemons.qbittorrent.QBitTorrentService.GetTorrents:input_type -> tstor.daemons.qbittorrent.GetTorrentsRequest
3, // 3: tstor.daemons.qbittorrent.QBitTorrentService.Cleanup:input_type -> tstor.daemons.qbittorrent.CleanupRequest
5, // 4: tstor.daemons.qbittorrent.QBitTorrentService.CleanupUnregistred:input_type -> tstor.daemons.qbittorrent.CleanupUnregistredRequest
2, // 5: tstor.daemons.qbittorrent.QBitTorrentService.GetTorrents:output_type -> tstor.daemons.qbittorrent.GetTorrentsResponse
4, // 6: tstor.daemons.qbittorrent.QBitTorrentService.Cleanup:output_type -> tstor.daemons.qbittorrent.CleanupResponse
6, // 7: tstor.daemons.qbittorrent.QBitTorrentService.CleanupUnregistred:output_type -> tstor.daemons.qbittorrent.CleanupUnregistredResponse
5, // [5:8] is the sub-list for method output_type
2, // [2:5] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
@ -429,26 +669,26 @@ var file_qbittorrent_proto_depIdxs = []int32{
0, // [0:2] is the sub-list for field type_name
}
func init() { file_qbittorrent_proto_init() }
func file_qbittorrent_proto_init() {
if File_qbittorrent_proto != nil {
func init() { file_proto_qbittorrent_proto_init() }
func file_proto_qbittorrent_proto_init() {
if File_proto_qbittorrent_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_qbittorrent_proto_rawDesc), len(file_qbittorrent_proto_rawDesc)),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_qbittorrent_proto_rawDesc), len(file_proto_qbittorrent_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_qbittorrent_proto_goTypes,
DependencyIndexes: file_qbittorrent_proto_depIdxs,
MessageInfos: file_qbittorrent_proto_msgTypes,
GoTypes: file_proto_qbittorrent_proto_goTypes,
DependencyIndexes: file_proto_qbittorrent_proto_depIdxs,
MessageInfos: file_proto_qbittorrent_proto_msgTypes,
}.Build()
File_qbittorrent_proto = out.File
file_qbittorrent_proto_goTypes = nil
file_qbittorrent_proto_depIdxs = nil
File_proto_qbittorrent_proto = out.File
file_proto_qbittorrent_proto_goTypes = nil
file_proto_qbittorrent_proto_depIdxs = nil
}

View file

@ -2,7 +2,7 @@
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.30.2
// source: qbittorrent.proto
// source: proto/qbittorrent.proto
package proto
@ -19,9 +19,9 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
QBitTorrentService_GetTorrents_FullMethodName = "/qbittorrent.QBitTorrentService/GetTorrents"
QBitTorrentService_Cleanup_FullMethodName = "/qbittorrent.QBitTorrentService/Cleanup"
QBitTorrentService_CleanupUnregistred_FullMethodName = "/qbittorrent.QBitTorrentService/CleanupUnregistred"
QBitTorrentService_GetTorrents_FullMethodName = "/tstor.daemons.qbittorrent.QBitTorrentService/GetTorrents"
QBitTorrentService_Cleanup_FullMethodName = "/tstor.daemons.qbittorrent.QBitTorrentService/Cleanup"
QBitTorrentService_CleanupUnregistred_FullMethodName = "/tstor.daemons.qbittorrent.QBitTorrentService/CleanupUnregistred"
)
// QBitTorrentServiceClient is the client API for QBitTorrentService service.
@ -174,7 +174,7 @@ func _QBitTorrentService_CleanupUnregistred_Handler(srv interface{}, ctx context
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var QBitTorrentService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "qbittorrent.QBitTorrentService",
ServiceName: "tstor.daemons.qbittorrent.QBitTorrentService",
HandlerType: (*QBitTorrentServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
@ -191,5 +191,5 @@ var QBitTorrentService_ServiceDesc = grpc.ServiceDesc{
},
},
Streams: []grpc.StreamDesc{},
Metadata: "qbittorrent.proto",
Metadata: "proto/qbittorrent.proto",
}

View file

@ -4,6 +4,7 @@ import (
"context"
"git.kmsign.ru/royalcat/tstor/server/src/daemon"
"git.kmsign.ru/royalcat/tstor/server/src/vfs"
"github.com/knadh/koanf/v2"
)
@ -12,5 +13,5 @@ type Plugin interface {
}
type DaemonPlugin interface {
NewDaemon(context.Context, *koanf.Koanf) (daemon.Daemon, error)
NewDaemon(context.Context, *koanf.Koanf, vfs.Filesystem) (daemon.Daemon, error)
}

View file

@ -75,6 +75,8 @@ func start(configPath string, plugins []Plugin) error {
log := rlog.Component("run")
sourceFS := vfs.NewOsFs(conf.SourceDir)
daemons := []daemon.Daemon{}
for _, plugin := range conf.Plugins {
@ -91,7 +93,7 @@ func start(configPath string, plugins []Plugin) error {
if daemonPlugin, ok := pluginStub.(DaemonPlugin); ok {
log.Debug(ctx, "plugin implements has daemon, creating", slog.String("name", pluginStub.Name()))
daemon, err := daemonPlugin.NewDaemon(ctx, pluginConfig)
daemon, err := daemonPlugin.NewDaemon(ctx, pluginConfig, sourceFS)
if err != nil {
log.Error(ctx, "error creating plugin daemon", rlog.Error(err))
continue
@ -114,7 +116,7 @@ func start(configPath string, plugins []Plugin) error {
// sourceFs := vfs.NewCtxBillyFs("/", ctxbilly.WrapFileSystem(osfs.New(conf.SourceDir, osfs.WithBoundOS())))
hostedfs, err := daemon.NewHostedFS(vfs.NewOsFs(conf.SourceDir), daemons)
hostedfs, err := daemon.NewHostedFS(sourceFS, daemons)
if err != nil {
return fmt.Errorf("error creating hosted filesystem: %w", err)
}