parent
10c3f126f0
commit
5b11b70370
32 changed files with 243 additions and 6575 deletions
daemons/atorrent
|
@ -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")))
|
||||
|
|
56
daemons/atorrent/generated/graphql/model/mappers.go
Normal file
56
daemons/atorrent/generated/graphql/model/mappers.go
Normal 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),
|
||||
}
|
||||
}
|
|
@ -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"`
|
||||
|
|
25
daemons/atorrent/generated/graphql/resolver/fs.resolvers.go
Normal file
25
daemons/atorrent/generated/graphql/resolver/fs.resolvers.go
Normal 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 }
|
17
daemons/atorrent/generated/graphql/resolver/resolver.go
Normal file
17
daemons/atorrent/generated/graphql/resolver/resolver.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 }
|
|
@ -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 }
|
|
@ -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 }
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue