remove old torrent for better times

This commit is contained in:
royalcat 2025-01-08 01:25:46 +03:00
parent 10c3f126f0
commit 5b11b70370
32 changed files with 243 additions and 6575 deletions

View file

@ -27,7 +27,7 @@ import (
"github.com/royalcat/kv"
)
const instrument = "git.kmsign.ru/royalcat/tstor/daemons/torrent"
const instrument = "git.kmsign.ru/royalcat/tstor/daemons/atorrent"
var (
tracer = otel.Tracer(instrument, trace.WithInstrumentationAttributes(attribute.String("component", "torrent-daemon")))

View file

@ -0,0 +1,56 @@
package model
import (
"context"
"git.kmsign.ru/royalcat/tstor/daemons/atorrent"
"github.com/anacrolix/torrent"
)
func MapPeerSource(source torrent.PeerSource) string {
switch source {
case torrent.PeerSourceDirect:
return "Direct"
case torrent.PeerSourceUtHolepunch:
return "Ut Holepunch"
case torrent.PeerSourceDhtAnnouncePeer:
return "DHT Announce"
case torrent.PeerSourceDhtGetPeers:
return "DHT"
case torrent.PeerSourceIncoming:
return "Incoming"
case torrent.PeerSourceTracker:
return "Tracker"
case torrent.PeerSourcePex:
return "PEX"
default:
return "Unknown"
}
}
func MapTorrent(ctx context.Context, t *atorrent.Controller) (*Torrent, error) {
prio, err := t.Priority(ctx)
if err != nil {
return nil, err
}
return &Torrent{
Infohash: t.InfoHash(),
Name: t.Name(),
BytesCompleted: t.BytesCompleted(),
BytesMissing: t.BytesMissing(),
Priority: prio,
T: t,
}, nil
}
func MapTorrentStats(s atorrent.TorrentStats) *TorrentStats {
return &TorrentStats{
Timestamp: s.Timestamp,
DownloadedBytes: uint(s.DownloadedBytes),
UploadedBytes: uint(s.UploadedBytes),
TotalPeers: uint(s.TotalPeers),
ActivePeers: uint(s.ActivePeers),
ConnectedSeeders: uint(s.ConnectedSeeders),
}
}

View file

@ -30,6 +30,12 @@ type FsEntry interface {
GetName() string
}
type Progress interface {
IsProgress()
GetCurrent() int64
GetTotal() int64
}
type ArchiveFs struct {
Name string `json:"name"`
Entries []FsEntry `json:"entries"`
@ -262,6 +268,16 @@ type TorrentPriorityFilter struct {
In []types.PiecePriority `json:"in,omitempty"`
}
type TorrentProgress struct {
Torrent *Torrent `json:"torrent"`
Current int64 `json:"current"`
Total int64 `json:"total"`
}
func (TorrentProgress) IsProgress() {}
func (this TorrentProgress) GetCurrent() int64 { return this.Current }
func (this TorrentProgress) GetTotal() int64 { return this.Total }
type TorrentStats struct {
Timestamp time.Time `json:"timestamp"`
DownloadedBytes uint `json:"downloadedBytes"`

View file

@ -0,0 +1,25 @@
package resolver
import (
"context"
"git.kmsign.ru/royalcat/tstor/daemons/atorrent/generated/graphql/model"
)
func (r *torrentFSResolver) Entries(ctx context.Context, obj *model.TorrentFs) ([]model.FsEntry, error) {
// entries, err := obj.FS.ReadDir(ctx, ".")
// if err != nil {
// return nil, err
// }
out := []model.FsEntry{}
// for _, e := range entries {
// entry, err := model.FillFsEntry(ctx, e, obj.FS, ".")
// if err != nil {
// return nil, err
// }
// out = append(out, entry)
// }
return out, nil
}
type torrentFSResolver struct{ *Resolver }

View file

@ -0,0 +1,17 @@
package resolver
import (
"git.kmsign.ru/royalcat/tstor/daemons/atorrent"
"git.kmsign.ru/royalcat/tstor/src/vfs"
"github.com/go-git/go-billy/v5"
)
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct {
ATorrentDaemon *atorrent.Daemon
VFS vfs.Filesystem
SourceFS billy.Filesystem
}

View file

@ -0,0 +1,49 @@
package resolver
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.55
import (
"context"
"fmt"
"git.kmsign.ru/royalcat/tstor/daemons/atorrent/generated/graphql/model"
)
type subscriptionResolver struct{ *Resolver }
func (r *subscriptionResolver) TorrentDownloadUpdates(ctx context.Context) (<-chan *model.TorrentProgress, error) {
out := make(chan *model.TorrentProgress)
progress, err := r.ATorrentDaemon.DownloadProgress(ctx)
if err != nil {
return nil, err
}
go func() {
defer close(out)
for p := range progress {
if p.Torrent == nil {
fmt.Println("nil torrent")
continue
}
torrent, err := model.MapTorrent(ctx, p.Torrent)
if err != nil {
// TODO logs
continue
}
po := &model.TorrentProgress{
Torrent: torrent,
Current: p.Current,
Total: p.Total,
}
select {
case <-ctx.Done():
return
case out <- po:
}
}
}()
return out, nil
}

View file

@ -0,0 +1,102 @@
package resolver
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.55
import (
"context"
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model"
"github.com/anacrolix/torrent/types"
)
// ValidateTorrent is the resolver for the validateTorrent field.
func (r *torrentDaemonMutationResolver) ValidateTorrent(ctx context.Context, obj *model.TorrentDaemonMutation, filter model.TorrentFilter) (bool, error) {
if filter.Infohash != nil {
t, err := r.Resolver.ATorrentDaemon.GetTorrent(*filter.Infohash)
if err != nil {
return false, err
}
if t == nil {
return false, nil
}
t.ValidateTorrent(ctx)
return true, nil
}
if filter.Everything != nil && *filter.Everything {
torrents, err := r.Resolver.ATorrentDaemon.ListTorrents(ctx)
if err != nil {
return false, err
}
for _, v := range torrents {
if err := v.ValidateTorrent(ctx); err != nil {
return false, err
}
}
return true, nil
}
return false, nil
}
// SetTorrentPriority is the resolver for the setTorrentPriority field.
func (r *torrentDaemonMutationResolver) SetTorrentPriority(ctx context.Context, obj *model.TorrentDaemonMutation, infohash string, file *string, priority types.PiecePriority) (bool, error) {
t, err := r.Resolver.ATorrentDaemon.GetTorrent(infohash)
if err != nil {
return false, err
}
if t == nil {
return false, nil
}
if file == nil {
err = t.SetPriority(ctx, priority)
if err != nil {
return false, err
}
return true, nil
}
f, err := t.GetFile(ctx, *file)
if err != nil {
return false, err
}
err = f.SetPriority(ctx, priority)
if err != nil {
return false, err
}
return true, nil
}
// Cleanup is the resolver for the cleanup field.
func (r *torrentDaemonMutationResolver) Cleanup(ctx context.Context, obj *model.TorrentDaemonMutation, files *bool, dryRun bool) (*model.CleanupResponse, error) {
torrents, err := r.ATorrentDaemon.ListTorrents(ctx)
if err != nil {
return nil, err
}
if files != nil && *files {
r, err := r.ATorrentDaemon.Storage.CleanupFiles(ctx, torrents, dryRun)
return &model.CleanupResponse{
Count: int64(len(r)),
List: r,
}, err
} else {
r, err := r.ATorrentDaemon.Storage.CleanupDirs(ctx, torrents, dryRun)
return &model.CleanupResponse{
Count: int64(len(r)),
List: r,
}, err
}
}
// TorrentDaemonMutation returns graph.TorrentDaemonMutationResolver implementation.
func (r *Resolver) TorrentDaemonMutation() graph.TorrentDaemonMutationResolver {
return &torrentDaemonMutationResolver{r}
}
type torrentDaemonMutationResolver struct{ *Resolver }

View file

@ -0,0 +1,142 @@
package resolver
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.55
import (
"context"
"time"
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model"
)
// Torrents is the resolver for the torrents field.
func (r *torrentDaemonQueryResolver) Torrents(ctx context.Context, obj *model.TorrentDaemonQuery, filter *model.TorrentsFilter) ([]*model.Torrent, error) {
// torrents, err := r.ATorrentDaemon.ListTorrents(ctx)
// if err != nil {
// return nil, err
// }
// filterFuncs := []func(torrent *model.Torrent) bool{}
// if filter != nil {
// if filter.BytesCompleted != nil {
// filterFuncs = append(filterFuncs, func(torrent *model.Torrent) bool {
// return filter.BytesCompleted.Include(torrent.BytesCompleted)
// })
// }
// if filter.BytesMissing != nil {
// filterFuncs = append(filterFuncs, func(torrent *model.Torrent) bool {
// return filter.BytesMissing.Include(torrent.BytesMissing)
// })
// }
// if filter.PeersCount != nil {
// filterFuncs = append(filterFuncs, func(torrent *model.Torrent) bool {
// return filter.PeersCount.Include(
// int64(len(torrent.T.Torrent().PeerConns())),
// )
// })
// }
// if filter.Infohash != nil {
// filterFuncs = append(filterFuncs, func(torrent *model.Torrent) bool {
// return filter.Infohash.Include(
// torrent.Infohash,
// )
// })
// }
// if filter.Priority != nil {
// filterFuncs = append(filterFuncs, func(torrent *model.Torrent) bool {
// return filter.Priority.Include(
// torrent.Priority,
// )
// })
// }
// }
// filterFunc := func(torrent *model.Torrent) bool {
// for _, f := range filterFuncs {
// if !f(torrent) {
// return false
// }
// }
// return true
// }
// tr := []*model.Torrent{}
// for _, t := range torrents {
// d, err := model.MapTorrent(ctx, t)
// if err != nil {
// return nil, err
// }
// if !filterFunc(d) {
// continue
// }
// tr = append(tr, d)
// }
// slices.SortStableFunc(torrents, func(t1, t2 *torrent.Controller) int {
// return strings.Compare(t1.Name(), t2.Name())
// })
// return tr, nil
panic("not implemented")
}
// ClientStats is the resolver for the clientStats field.
func (r *torrentDaemonQueryResolver) ClientStats(ctx context.Context, obj *model.TorrentDaemonQuery) (*model.TorrentClientStats, error) {
// stats := r.ATorrentDaemon.Stats()
// return &model.TorrentClientStats{
// BytesWritten: stats.BytesWritten.Int64(),
// BytesRead: stats.BytesRead.Int64(),
// BytesWrittenData: stats.BytesWrittenData.Int64(),
// BytesReadData: stats.BytesReadData.Int64(),
// BytesReadUsefulData: stats.BytesReadUsefulData.Int64(),
// BytesReadUsefulIntendedData: stats.BytesReadUsefulIntendedData.Int64(),
// ChunksWritten: stats.ChunksWritten.Int64(),
// ChunksRead: stats.ChunksRead.Int64(),
// ChunksReadUseful: stats.ChunksReadUseful.Int64(),
// ChunksReadWasted: stats.ChunksReadWasted.Int64(),
// MetadataChunksRead: stats.MetadataChunksRead.Int64(),
// PiecesDirtiedGood: stats.PiecesDirtiedGood.Int64(),
// PiecesDirtiedBad: stats.PiecesDirtiedBad.Int64(),
// }, nil
panic("not implemented")
}
// StatsHistory is the resolver for the statsHistory field.
func (r *torrentDaemonQueryResolver) StatsHistory(ctx context.Context, obj *model.TorrentDaemonQuery, since time.Time, infohash *string) ([]*model.TorrentStats, error) {
// var stats []torrent.TorrentStats
// if infohash == nil {
// stats, err := r.ATorrentDaemon.StatsHistory(ctx, since)
// if err != nil {
// return nil, err
// }
// return model.Apply(stats, model.MapTorrentStats), nil
// } else if *infohash == "total" {
// var err error
// stats, err = r.ATorrentDaemon.TotalStatsHistory(ctx, since)
// if err != nil {
// return nil, err
// }
// } else {
// ih := tinfohash.FromHexString(*infohash)
// var err error
// stats, err = r.ATorrentDaemon.TorrentStatsHistory(ctx, since, ih)
// if err != nil {
// return nil, err
// }
// }
// return model.Apply(stats, model.MapTorrentStats), nil
panic("not implemented")
}
// TorrentDaemonQuery returns graph.TorrentDaemonQueryResolver implementation.
func (r *Resolver) TorrentDaemonQuery() graph.TorrentDaemonQueryResolver {
return &torrentDaemonQueryResolver{r}
}
type torrentDaemonQueryResolver struct{ *Resolver }

View file

@ -0,0 +1,90 @@
package resolver
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.55
import (
"context"
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model"
"github.com/anacrolix/torrent/types"
)
// Name is the resolver for the name field.
func (r *torrentResolver) Name(ctx context.Context, obj *model.Torrent) (string, error) {
return obj.T.Name(), nil
}
// Files is the resolver for the files field.
func (r *torrentResolver) Files(ctx context.Context, obj *model.Torrent) ([]*model.TorrentFile, error) {
out := []*model.TorrentFile{}
files, err := obj.T.Files(ctx)
if err != nil {
return nil, err
}
for _, f := range files {
out = append(out, &model.TorrentFile{
Filename: f.Path(),
Size: f.Size(),
BytesCompleted: f.BytesCompleted(),
F: f,
})
}
return out, nil
}
// ExcludedFiles is the resolver for the excludedFiles field.
func (r *torrentResolver) ExcludedFiles(ctx context.Context, obj *model.Torrent) ([]*model.TorrentFile, error) {
out := []*model.TorrentFile{}
// files, err := obj.T.ExcludedFiles()
// if err != nil {
// return nil, err
// }
// for _, f := range files {
// out = append(out, &model.TorrentFile{
// Filename: f.DisplayPath(),
// Size: f.Length(),
// F: f,
// })
// }
return out, nil
}
// Peers is the resolver for the peers field.
func (r *torrentResolver) Peers(ctx context.Context, obj *model.Torrent) ([]*model.TorrentPeer, error) {
peers := []*model.TorrentPeer{}
for _, peer := range obj.T.Torrent().PeerConns() {
clientName, _ := peer.PeerClientName.Load().(string)
peers = append(peers, &model.TorrentPeer{
IP: peer.RemoteAddr.String(),
DownloadRate: peer.DownloadRate(),
Discovery: model.MapPeerSource(peer.Discovery),
Port: int64(peer.PeerListenPort),
ClientName: clientName,
F: peer,
})
}
return peers, nil
}
// Priority is the resolver for the priority field.
func (r *torrentFileResolver) Priority(ctx context.Context, obj *model.TorrentFile) (types.PiecePriority, error) {
props, err := obj.F.Properties(ctx)
if err != nil {
return 0, err
}
return props.Priority, nil
}
// Torrent returns graph.TorrentResolver implementation.
func (r *Resolver) Torrent() graph.TorrentResolver { return &torrentResolver{r} }
// TorrentFile returns graph.TorrentFileResolver implementation.
func (r *Resolver) TorrentFile() graph.TorrentFileResolver { return &torrentFileResolver{r} }
type torrentResolver struct{ *Resolver }
type torrentFileResolver struct{ *Resolver }

View file

@ -22,7 +22,9 @@ require (
)
require (
github.com/99designs/gqlgen v0.17.55 // indirect
github.com/RoaringBitmap/roaring v1.2.3 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/anacrolix/chansync v0.4.1-0.20240627045151-1aa1ac392fe8 // indirect
@ -115,10 +117,12 @@ require (
github.com/samber/lo v1.38.1 // indirect
github.com/samber/slog-multi v1.0.2 // indirect
github.com/samber/slog-zerolog v1.0.0 // indirect
github.com/sosodev/duration v1.3.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/tidwall/btree v1.6.0 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/vektah/gqlparser/v2 v2.5.17 // indirect
github.com/willscott/go-nfs-client v0.0.0-20240104095149-b44639837b00 // indirect
github.com/wlynxg/anet v0.0.3 // indirect
go.etcd.io/bbolt v1.3.6 // indirect

View file

@ -21,6 +21,8 @@ filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmG
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.kmsign.ru/royalcat/tstor v0.0.0-20241217095110-0ae11aa283e3 h1:ET5ZglQhYrJNxWJ0XNvNj+RsC3ajFJntvuJqAEXak+4=
git.kmsign.ru/royalcat/tstor v0.0.0-20241217095110-0ae11aa283e3/go.mod h1:8gdepJ0vUeAfV/xiWMo6zj2DYx4qjc4XdhGvy77Nxy0=
github.com/99designs/gqlgen v0.17.55 h1:3vzrNWYyzSZjGDFo68e5j9sSauLxfKvLp+6ioRokVtM=
github.com/99designs/gqlgen v0.17.55/go.mod h1:3Bq768f8hgVPGZxL8aY9MaYmbxa6llPM/qu1IGH1EJo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
@ -32,6 +34,8 @@ github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVO
github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 h1:byYvvbfSo3+9efR4IeReh77gVs4PnNDR3AMOE9NJ7a0=
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT41qKc048STsifIt69LfUJ8SrWWcz/yam5k=
github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk=
@ -99,9 +103,13 @@ github.com/anacrolix/upnp v0.1.4 h1:+2t2KA6QOhm/49zeNyeVwDu1ZYS9dB9wfxyVvh/wk7U=
github.com/anacrolix/upnp v0.1.4/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic=
github.com/anacrolix/utp v0.1.0 h1:FOpQOmIwYsnENnz7tAGohA+r6iXpRjrq8ssKSre2Cp4=
github.com/anacrolix/utp v0.1.0/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
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=
@ -149,6 +157,8 @@ github.com/dgraph-io/ristretto/v2 v2.0.0/go.mod h1:FVFokF2dRqXyPyeMnK1YDy8Fc6aTe
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -449,12 +459,16 @@ github.com/samber/slog-multi v1.0.2 h1:6BVH9uHGAsiGkbbtQgAOQJMpKgV8unMrHhhJaw+X1
github.com/samber/slog-multi v1.0.2/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo=
github.com/samber/slog-zerolog v1.0.0 h1:YpRy0xux1uJr0Ng3wrEjv9nyvb4RAoNqkS611UjzeG8=
github.com/samber/slog-zerolog v1.0.0/go.mod h1:N2/g/mNGRY1zqsydIYE0uKipSSFsPDjytoVkRnZ0Jp0=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@ -484,6 +498,8 @@ github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDW
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vektah/gqlparser/v2 v2.5.17 h1:9At7WblLV7/36nulgekUgIaqHZWn5hxqluxrxGUhOmI=
github.com/vektah/gqlparser/v2 v2.5.17/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww=
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willscott/go-nfs-client v0.0.0-20240104095149-b44639837b00 h1:U0DnHRZFzoIV1oFEZczg5XyPut9yxk9jjtax/9Bxr/o=
@ -744,6 +760,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -1,3 +1,21 @@
type TorrentFS implements Dir & FsEntry {
name: String!
torrent: Torrent!
entries: [FsEntry!]! @resolver
}
type TorrentFileEntry implements File & FsEntry {
name: String!
torrent: Torrent!
size: Int!
}
type TorrentProgress implements Progress {
torrent: Torrent!
current: Int!
total: Int!
}
type Torrent {
name: String! @resolver
infohash: String!

View file

@ -88,7 +88,7 @@ func newPieceCompletion(dir string) (storage.PieceCompletion, error) {
opts.Codec = kv.CodecBinary[PieceCompletionState, *PieceCompletionState]{}
opts.BadgerOptions = opts.BadgerOptions.WithLogger(logwrap.BadgerLogger("torrent-client", "piece-completion"))
db, err := kvbadger.NewBagerKVBinaryKey[pieceKey, PieceCompletionState](opts)
db, err := kvbadger.NewBinaryKey[pieceKey, PieceCompletionState](opts)
if err != nil {
return nil, err
}