parent
c496c0269a
commit
10c3f126f0
42 changed files with 1910 additions and 694 deletions
src/delivery
|
@ -3,7 +3,6 @@ package model
|
|||
import (
|
||||
"context"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
|
||||
"git.kmsign.ru/royalcat/tstor/src/vfs"
|
||||
)
|
||||
|
||||
|
@ -27,17 +26,17 @@ func FillFsEntry(ctx context.Context, e FsElem, fs vfs.Filesystem, path string)
|
|||
Name: e.Name(),
|
||||
FS: e,
|
||||
}, nil
|
||||
case *torrent.TorrentFS:
|
||||
e := e.(*torrent.TorrentFS)
|
||||
torrent, err := MapTorrent(ctx, e.Torrent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return TorrentFs{
|
||||
Name: e.Name(),
|
||||
Torrent: torrent,
|
||||
FS: e,
|
||||
}, nil
|
||||
// case *torrent.TorrentFS:
|
||||
// e := e.(*torrent.TorrentFS)
|
||||
// torrent, err := MapTorrent(ctx, e.Torrent)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return TorrentFs{
|
||||
// Name: e.Name(),
|
||||
// Torrent: torrent,
|
||||
// FS: e,
|
||||
// }, nil
|
||||
default:
|
||||
if e.IsDir() {
|
||||
return SimpleDir{
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
|
||||
atorrent "github.com/anacrolix/torrent"
|
||||
)
|
||||
|
||||
func Apply[I any, O any](in []I, f func(I) O) []O {
|
||||
out := make([]O, len(in))
|
||||
for i, v := range in {
|
||||
|
@ -15,50 +8,50 @@ func Apply[I any, O any](in []I, f func(I) O) []O {
|
|||
return out
|
||||
}
|
||||
|
||||
func MapPeerSource(source atorrent.PeerSource) string {
|
||||
switch source {
|
||||
case atorrent.PeerSourceDirect:
|
||||
return "Direct"
|
||||
case atorrent.PeerSourceUtHolepunch:
|
||||
return "Ut Holepunch"
|
||||
case atorrent.PeerSourceDhtAnnouncePeer:
|
||||
return "DHT Announce"
|
||||
case atorrent.PeerSourceDhtGetPeers:
|
||||
return "DHT"
|
||||
case atorrent.PeerSourceIncoming:
|
||||
return "Incoming"
|
||||
case atorrent.PeerSourceTracker:
|
||||
return "Tracker"
|
||||
case atorrent.PeerSourcePex:
|
||||
return "PEX"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
// func MapPeerSource(source atorrent.PeerSource) string {
|
||||
// switch source {
|
||||
// case atorrent.PeerSourceDirect:
|
||||
// return "Direct"
|
||||
// case atorrent.PeerSourceUtHolepunch:
|
||||
// return "Ut Holepunch"
|
||||
// case atorrent.PeerSourceDhtAnnouncePeer:
|
||||
// return "DHT Announce"
|
||||
// case atorrent.PeerSourceDhtGetPeers:
|
||||
// return "DHT"
|
||||
// case atorrent.PeerSourceIncoming:
|
||||
// return "Incoming"
|
||||
// case atorrent.PeerSourceTracker:
|
||||
// return "Tracker"
|
||||
// case atorrent.PeerSourcePex:
|
||||
// return "PEX"
|
||||
// default:
|
||||
// return "Unknown"
|
||||
// }
|
||||
// }
|
||||
|
||||
func MapTorrent(ctx context.Context, t *torrent.Controller) (*Torrent, error) {
|
||||
prio, err := t.Priority(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// func MapTorrent(ctx context.Context, t *torrent.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
|
||||
}
|
||||
// return &Torrent{
|
||||
// Infohash: t.InfoHash(),
|
||||
// Name: t.Name(),
|
||||
// BytesCompleted: t.BytesCompleted(),
|
||||
// BytesMissing: t.BytesMissing(),
|
||||
// Priority: prio,
|
||||
// T: t,
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
func MapTorrentStats(s torrent.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),
|
||||
}
|
||||
}
|
||||
// func MapTorrentStats(s torrent.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),
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -2,7 +2,6 @@ package resolver
|
|||
|
||||
import (
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
|
||||
"git.kmsign.ru/royalcat/tstor/src/vfs"
|
||||
"github.com/go-git/go-billy/v5"
|
||||
)
|
||||
|
@ -12,7 +11,7 @@ import (
|
|||
// It serves as dependency injection for your app, add any dependencies you require here.
|
||||
|
||||
type Resolver struct {
|
||||
ATorrentDaemon *torrent.Daemon
|
||||
// ATorrentDaemon *torrent.Daemon
|
||||
QBitTorrentDaemon *qbittorrent.Daemon
|
||||
VFS vfs.Filesystem
|
||||
SourceFS billy.Filesystem
|
||||
|
|
|
@ -6,133 +6,132 @@ package resolver
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
|
||||
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
|
||||
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model"
|
||||
tinfohash "github.com/anacrolix/torrent/types/infohash"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
// torrents, err := r.ATorrentDaemon.ListTorrents(ctx)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
filterFuncs := []func(torrent *model.Torrent) bool{}
|
||||
// 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,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
// 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
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// if !filterFunc(d) {
|
||||
// continue
|
||||
// }
|
||||
// tr = append(tr, d)
|
||||
// }
|
||||
|
||||
slices.SortStableFunc(torrents, func(t1, t2 *torrent.Controller) int {
|
||||
return strings.Compare(t1.Name(), t2.Name())
|
||||
})
|
||||
// slices.SortStableFunc(torrents, func(t1, t2 *torrent.Controller) int {
|
||||
// return strings.Compare(t1.Name(), t2.Name())
|
||||
// })
|
||||
|
||||
return tr, nil
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
}
|
||||
// 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
|
||||
// return model.Apply(stats, model.MapTorrentStats), nil
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// TorrentDaemonQuery returns graph.TorrentDaemonQueryResolver implementation.
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"net/http"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
|
||||
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
|
||||
"git.kmsign.ru/royalcat/tstor/src/config"
|
||||
"git.kmsign.ru/royalcat/tstor/src/vfs"
|
||||
|
@ -16,7 +15,7 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func Run(torrentdaemon *torrent.Daemon, qbitdaemon *qbittorrent.Daemon, vfs vfs.Filesystem, cfg *config.Settings) error {
|
||||
func Run(qbitdaemon *qbittorrent.Daemon, vfs vfs.Filesystem, cfg *config.Settings) error {
|
||||
log := slog.With()
|
||||
|
||||
r := echo.New()
|
||||
|
@ -29,7 +28,7 @@ func Run(torrentdaemon *torrent.Daemon, qbitdaemon *qbittorrent.Daemon, vfs vfs.
|
|||
|
||||
echopprof.Register(r)
|
||||
|
||||
r.Any("/graphql", echo.WrapHandler((GraphQLHandler(torrentdaemon, qbitdaemon, vfs))))
|
||||
r.Any("/graphql", echo.WrapHandler((GraphQLHandler(qbitdaemon, vfs))))
|
||||
r.GET("/metrics", echo.WrapHandler(promhttp.Handler()))
|
||||
|
||||
log.Info("starting webserver", "host", fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port))
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"net/http"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
|
||||
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
|
||||
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
|
||||
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/resolver"
|
||||
"git.kmsign.ru/royalcat/tstor/src/vfs"
|
||||
|
@ -21,12 +20,11 @@ func noopDirective(ctx context.Context, obj interface{}, next graphql.Resolver)
|
|||
return next(ctx)
|
||||
}
|
||||
|
||||
func GraphQLHandler(service *torrent.Daemon, qbitdaemon *qbittorrent.Daemon, vfs vfs.Filesystem) http.Handler {
|
||||
func GraphQLHandler(qbitdaemon *qbittorrent.Daemon, vfs vfs.Filesystem) http.Handler {
|
||||
graphqlHandler := handler.NewDefaultServer(
|
||||
graph.NewExecutableSchema(
|
||||
graph.Config{
|
||||
Resolvers: &resolver.Resolver{
|
||||
ATorrentDaemon: service,
|
||||
QBitTorrentDaemon: qbitdaemon,
|
||||
VFS: vfs,
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue