chore: Refactor code to use SourceUpdater struct for managing sources
This commit is contained in:
parent
fa1fdcfc63
commit
99cdd5471e
12 changed files with 328 additions and 263 deletions
src
delivery/graphql/resolver
host
|
@ -93,9 +93,7 @@ func (r *mutationResolver) DownloadTorrent(ctx context.Context, infohash string,
|
|||
|
||||
// UploadFile is the resolver for the uploadFile field.
|
||||
func (r *mutationResolver) UploadFile(ctx context.Context, dir string, file graphql.Upload) (bool, error) {
|
||||
dir = pathlib.Join(r.Service.SourceDir, dir)
|
||||
|
||||
dirInfo, err := os.Stat(dir)
|
||||
dirInfo, err := r.SourceFS.Stat(dir)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -105,7 +103,7 @@ func (r *mutationResolver) UploadFile(ctx context.Context, dir string, file grap
|
|||
}
|
||||
|
||||
filename := pathlib.Join(dir, file.Filename)
|
||||
target, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, os.ModePerm)
|
||||
target, err := r.SourceFS.OpenFile(filename, os.O_CREATE|os.O_WRONLY, os.ModePerm)
|
||||
defer target.Close()
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -3,6 +3,7 @@ package resolver
|
|||
import (
|
||||
"git.kmsign.ru/royalcat/tstor/src/host/service"
|
||||
"git.kmsign.ru/royalcat/tstor/src/host/vfs"
|
||||
"github.com/go-git/go-billy/v5"
|
||||
)
|
||||
|
||||
// This file will not be regenerated automatically.
|
||||
|
@ -10,6 +11,7 @@ import (
|
|||
// It serves as dependency injection for your app, add any dependencies you require here.
|
||||
|
||||
type Resolver struct {
|
||||
Service *service.Service
|
||||
VFS vfs.Filesystem
|
||||
Service *service.Service
|
||||
VFS vfs.Filesystem
|
||||
SourceFS billy.Filesystem
|
||||
}
|
||||
|
|
|
@ -6,6 +6,10 @@ import (
|
|||
"github.com/lrstanley/go-ytdlp"
|
||||
)
|
||||
|
||||
type SourceUpdater struct {
|
||||
sources []VirtDirSource
|
||||
}
|
||||
|
||||
type SourcedDirSource string
|
||||
|
||||
const (
|
||||
|
|
|
@ -17,7 +17,7 @@ type TorrentDownloadTask struct {
|
|||
}
|
||||
|
||||
func (s *Service) Download(ctx context.Context, task *TorrentDownloadTask) error {
|
||||
t, ok := s.c.Torrent(task.InfoHash)
|
||||
t, ok := s.client.Torrent(task.InfoHash)
|
||||
if !ok {
|
||||
return fmt.Errorf("torrent with IH %s not found", task.InfoHash.HexString())
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/pkg/ctxio"
|
||||
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
|
||||
|
@ -28,8 +29,9 @@ import (
|
|||
"github.com/anacrolix/torrent"
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"github.com/anacrolix/torrent/types"
|
||||
"github.com/anacrolix/torrent/types/infohash"
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/util"
|
||||
"github.com/royalcat/kv"
|
||||
)
|
||||
|
||||
|
@ -41,45 +43,68 @@ type DirAquire struct {
|
|||
}
|
||||
|
||||
type Service struct {
|
||||
c *torrent.Client
|
||||
client *torrent.Client
|
||||
excludedFiles *store.FilesMappings
|
||||
infoBytes *store.InfoBytes
|
||||
Storage *datastorage.DataStorage
|
||||
fis *store.FileItemStore
|
||||
dirsAquire kv.Store[string, DirAquire]
|
||||
|
||||
loadMutex sync.Mutex
|
||||
torrentLoaded chan struct{}
|
||||
|
||||
loadMutex sync.Mutex
|
||||
|
||||
// stats *Stats
|
||||
DefaultPriority types.PiecePriority
|
||||
Storage *datastorage.DataStorage
|
||||
SourceDir string
|
||||
|
||||
dirsAquire kv.Store[string, DirAquire]
|
||||
sourceFs billy.Filesystem
|
||||
|
||||
log *rlog.Logger
|
||||
}
|
||||
|
||||
func NewService(sourceDir string, cfg config.TorrentClient, c *torrent.Client,
|
||||
storage *datastorage.DataStorage, excludedFiles *store.FilesMappings, infoBytes *store.InfoBytes,
|
||||
) (*Service, error) {
|
||||
dirsAcquire, err := tkv.New[string, DirAquire](cfg.MetadataFolder, "dir-acquire")
|
||||
func New(sourceFs billy.Filesystem, conf config.TorrentClient) (*Service, error) {
|
||||
s := &Service{
|
||||
log: rlog.Component("torrent-service"),
|
||||
sourceFs: sourceFs,
|
||||
torrentLoaded: make(chan struct{}),
|
||||
loadMutex: sync.Mutex{},
|
||||
}
|
||||
|
||||
err := os.MkdirAll(conf.MetadataFolder, 0744)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating metadata folder: %w", err)
|
||||
}
|
||||
|
||||
s.fis, err = store.NewFileItemStore(filepath.Join(conf.MetadataFolder, "items"), 2*time.Hour)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error starting item store: %w", err)
|
||||
}
|
||||
|
||||
s.Storage, _, err = datastorage.Setup(conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
log: rlog.Component("torrent-service"),
|
||||
c: c,
|
||||
DefaultPriority: types.PiecePriorityNone,
|
||||
excludedFiles: excludedFiles,
|
||||
infoBytes: infoBytes,
|
||||
Storage: storage,
|
||||
SourceDir: sourceDir,
|
||||
torrentLoaded: make(chan struct{}),
|
||||
loadMutex: sync.Mutex{},
|
||||
dirsAquire: dirsAcquire,
|
||||
s.excludedFiles, err = store.NewFileMappings(conf.MetadataFolder, s.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// stats: newStats(), // TODO persistent
|
||||
s.infoBytes, err = store.NewInfoBytes(conf.MetadataFolder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := store.GetOrCreatePeerID(filepath.Join(conf.MetadataFolder, "ID"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating node ID: %w", err)
|
||||
}
|
||||
|
||||
client, err := store.NewClient(s.Storage, s.fis, &conf, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error starting torrent client: %w", err)
|
||||
}
|
||||
client.AddDhtNodes(conf.DHTNodes)
|
||||
|
||||
s.dirsAquire, err = tkv.New[string, DirAquire](conf.MetadataFolder, "dir-acquire")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
@ -96,11 +121,14 @@ func NewService(sourceDir string, cfg config.TorrentClient, c *torrent.Client,
|
|||
|
||||
var _ vfs.FsFactory = (*Service)(nil).NewTorrentFs
|
||||
|
||||
func (s *Service) Close() error {
|
||||
|
||||
func (s *Service) Close(ctx context.Context) error {
|
||||
return errors.Join(append(
|
||||
s.c.Close(),
|
||||
s.client.Close(),
|
||||
s.Storage.Close(),
|
||||
s.dirsAquire.Close(ctx),
|
||||
s.excludedFiles.Close(ctx),
|
||||
s.infoBytes.Close(),
|
||||
s.fis.Close(),
|
||||
)...)
|
||||
}
|
||||
|
||||
|
@ -123,7 +151,7 @@ func (s *Service) LoadTorrent(ctx context.Context, f vfs.File) (*torrent.Torrent
|
|||
return nil, fmt.Errorf("loading torrent metadata from file %s, error: %w", stat.Name(), err)
|
||||
}
|
||||
|
||||
t, ok := s.c.Torrent(mi.HashInfoBytes())
|
||||
t, ok := s.client.Torrent(mi.HashInfoBytes())
|
||||
if !ok {
|
||||
|
||||
span.AddEvent("torrent not found, loading from file")
|
||||
|
@ -148,7 +176,7 @@ func (s *Service) LoadTorrent(ctx context.Context, f vfs.File) (*torrent.Torrent
|
|||
}
|
||||
}
|
||||
|
||||
t, _ = s.c.AddTorrentOpt(torrent.AddTorrentOpts{
|
||||
t, _ = s.client.AddTorrentOpt(torrent.AddTorrentOpts{
|
||||
InfoHash: spec.InfoHash,
|
||||
Storage: s.Storage,
|
||||
InfoBytes: infoBytes,
|
||||
|
@ -223,7 +251,7 @@ func (s *Service) checkTorrentCompatable(ctx context.Context, ih infohash.T, inf
|
|||
return true, false, nil
|
||||
}
|
||||
|
||||
for _, existingTorrent := range s.c.Torrents() {
|
||||
for _, existingTorrent := range s.client.Torrents() {
|
||||
if existingTorrent.Name() != name || existingTorrent.InfoHash() == ih {
|
||||
continue
|
||||
}
|
||||
|
@ -344,7 +372,7 @@ func (s *Service) Stats() (*Stats, error) {
|
|||
}
|
||||
|
||||
func (s *Service) GetStats() torrent.ConnStats {
|
||||
return s.c.ConnStats()
|
||||
return s.client.ConnStats()
|
||||
}
|
||||
|
||||
const loadWorkers = 5
|
||||
|
@ -386,7 +414,7 @@ func (s *Service) loadTorrentFiles(ctx context.Context) error {
|
|||
go loaderWorker()
|
||||
}
|
||||
|
||||
return filepath.Walk(s.SourceDir, func(path string, info os.FileInfo, err error) error {
|
||||
return util.Walk(s.sourceFs, "/", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("fs walk error: %w", err)
|
||||
}
|
||||
|
@ -411,7 +439,7 @@ func (s *Service) ListTorrents(ctx context.Context) ([]*controller.Torrent, erro
|
|||
<-s.torrentLoaded
|
||||
|
||||
out := []*controller.Torrent{}
|
||||
for _, v := range s.c.Torrents() {
|
||||
for _, v := range s.client.Torrents() {
|
||||
out = append(out, controller.NewTorrent(v, s.excludedFiles))
|
||||
}
|
||||
return out, nil
|
||||
|
@ -420,7 +448,7 @@ func (s *Service) ListTorrents(ctx context.Context) ([]*controller.Torrent, erro
|
|||
func (s *Service) GetTorrent(infohashHex string) (*controller.Torrent, error) {
|
||||
<-s.torrentLoaded
|
||||
|
||||
t, ok := s.c.Torrent(infohash.FromHexString(infohashHex))
|
||||
t, ok := s.client.Torrent(infohash.FromHexString(infohashHex))
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"git.kmsign.ru/royalcat/tstor/src/host/vfs"
|
||||
)
|
||||
|
||||
func NewTorrentStorage(dataPath string, tsrv *service.Service) vfs.Filesystem {
|
||||
func NewTorrentStorage(sourceFS vfs.Filesystem, tsrv *service.Service) vfs.Filesystem {
|
||||
factories := map[string]vfs.FsFactory{
|
||||
".torrent": tsrv.NewTorrentFs,
|
||||
}
|
||||
|
@ -15,5 +15,5 @@ func NewTorrentStorage(dataPath string, tsrv *service.Service) vfs.Filesystem {
|
|||
factories[k] = v
|
||||
}
|
||||
|
||||
return vfs.NewResolveFS(vfs.NewOsFs(dataPath), factories)
|
||||
return vfs.NewResolveFS(sourceFS, factories)
|
||||
}
|
||||
|
|
|
@ -55,3 +55,7 @@ func (r *FilesMappings) FileMappings(ctx context.Context, ih infohash.T) (map[st
|
|||
})
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (r *FilesMappings) Close(ctx context.Context) error {
|
||||
return r.mappings.Close(ctx)
|
||||
}
|
||||
|
|
146
src/host/vfs/ctxbillyfs.go
Normal file
146
src/host/vfs/ctxbillyfs.go
Normal file
|
@ -0,0 +1,146 @@
|
|||
package vfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/pkg/ctxbilly"
|
||||
)
|
||||
|
||||
func NewCtxBillyFs(name string, fs ctxbilly.Filesystem) *CtxBillyFs {
|
||||
return &CtxBillyFs{
|
||||
name: name,
|
||||
fs: fs,
|
||||
}
|
||||
}
|
||||
|
||||
type CtxBillyFs struct {
|
||||
name string
|
||||
fs ctxbilly.Filesystem
|
||||
}
|
||||
|
||||
var _ Filesystem = (*CtxBillyFs)(nil)
|
||||
|
||||
// Info implements Filesystem.
|
||||
func (c *CtxBillyFs) Info() (fs.FileInfo, error) {
|
||||
return c.fs.Stat(context.Background(), "")
|
||||
}
|
||||
|
||||
// IsDir implements Filesystem.
|
||||
func (c *CtxBillyFs) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Name implements Filesystem.
|
||||
func (c *CtxBillyFs) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
// Open implements Filesystem.
|
||||
func (c *CtxBillyFs) Open(ctx context.Context, filename string) (File, error) {
|
||||
info, err := c.fs.Stat(ctx, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bf, err := c.fs.Open(ctx, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CtxBillyFile{
|
||||
info: info,
|
||||
file: bf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ReadDir implements Filesystem.
|
||||
func (c *CtxBillyFs) ReadDir(ctx context.Context, path string) ([]fs.DirEntry, error) {
|
||||
infos, err := c.fs.ReadDir(ctx, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries := make([]fs.DirEntry, 0, len(infos))
|
||||
for _, i := range infos {
|
||||
entries = append(entries, &infoEntry{i})
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
type infoEntry struct {
|
||||
fs.FileInfo
|
||||
}
|
||||
|
||||
var _ fs.DirEntry = (*infoEntry)(nil)
|
||||
|
||||
// Info implements fs.DirEntry.
|
||||
func (i *infoEntry) Info() (fs.FileInfo, error) {
|
||||
return i.FileInfo, nil
|
||||
}
|
||||
|
||||
// Type implements fs.DirEntry.
|
||||
func (i *infoEntry) Type() fs.FileMode {
|
||||
return i.FileInfo.Mode()
|
||||
}
|
||||
|
||||
// Stat implements Filesystem.
|
||||
func (c *CtxBillyFs) Stat(ctx context.Context, filename string) (fs.FileInfo, error) {
|
||||
return c.fs.Stat(ctx, filename)
|
||||
}
|
||||
|
||||
// Type implements Filesystem.
|
||||
func (c *CtxBillyFs) Type() fs.FileMode {
|
||||
return fs.ModeDir
|
||||
}
|
||||
|
||||
// Unlink implements Filesystem.
|
||||
func (c *CtxBillyFs) Unlink(ctx context.Context, filename string) error {
|
||||
return fs.ErrInvalid
|
||||
}
|
||||
|
||||
var _ File = (*CtxBillyFile)(nil)
|
||||
|
||||
type CtxBillyFile struct {
|
||||
info fs.FileInfo
|
||||
file ctxbilly.File
|
||||
}
|
||||
|
||||
// Close implements File.
|
||||
func (c *CtxBillyFile) Close(ctx context.Context) error {
|
||||
return c.file.Close(ctx)
|
||||
}
|
||||
|
||||
// Info implements File.
|
||||
func (c *CtxBillyFile) Info() (fs.FileInfo, error) {
|
||||
return c.info, nil
|
||||
}
|
||||
|
||||
// IsDir implements File.
|
||||
func (c *CtxBillyFile) IsDir() bool {
|
||||
return c.info.IsDir()
|
||||
}
|
||||
|
||||
// Name implements File.
|
||||
func (c *CtxBillyFile) Name() string {
|
||||
return c.file.Name()
|
||||
}
|
||||
|
||||
// Read implements File.
|
||||
func (c *CtxBillyFile) Read(ctx context.Context, p []byte) (n int, err error) {
|
||||
return c.file.Read(ctx, p)
|
||||
}
|
||||
|
||||
// ReadAt implements File.
|
||||
func (c *CtxBillyFile) ReadAt(ctx context.Context, p []byte, off int64) (n int, err error) {
|
||||
return c.file.ReadAt(ctx, p, off)
|
||||
}
|
||||
|
||||
// Size implements File.
|
||||
func (c *CtxBillyFile) Size() int64 {
|
||||
return c.info.Size()
|
||||
}
|
||||
|
||||
// Type implements File.
|
||||
func (c *CtxBillyFile) Type() fs.FileMode {
|
||||
return c.info.Mode()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue