This commit is contained in:
royalcat 2024-04-24 20:36:33 +03:00
parent 5591f145a9
commit d8ee8a3a24
166 changed files with 15431 additions and 889 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
package model
import (
"git.kmsign.ru/royalcat/tstor/src/host/vfs"
)
type FsElem interface {
Name() string
IsDir() bool
}
func FillFsEntry(e FsElem, fs vfs.Filesystem, path string) FsEntry {
switch e.(type) {
case *vfs.ArchiveFS:
e := e.(*vfs.ArchiveFS)
return ArchiveFs{
Name: e.Name(),
Size: e.Size(),
FS: e,
}
case *vfs.ResolverFS:
e := e.(*vfs.ResolverFS)
return ResolverFs{
Name: e.Name(),
FS: e,
}
case *vfs.TorrentFS:
e := e.(*vfs.TorrentFS)
return TorrentFs{
Name: e.Name(),
Torrent: MapTorrent(e.Torrent),
FS: e,
}
default:
if e.IsDir() {
return SimpleDir{
Name: e.Name(),
FS: fs,
Path: path,
}
} else {
return SimpleFile{
Name: e.Name(),
}
}
}
}

View file

@ -1,9 +1,18 @@
package model
import "slices"
import (
"slices"
"strings"
)
type Filter[T any] interface {
Include(v T) bool
}
func (f *IntFilter) Include(v int64) bool {
if f.Eq != nil {
if f == nil {
return true
} else if f.Eq != nil {
return v == *f.Eq
} else if f.Gt != nil {
return v > *f.Gt
@ -19,3 +28,17 @@ func (f *IntFilter) Include(v int64) bool {
return true
}
func (f *StringFilter) Include(v string) bool {
if f == nil {
return true
} else if f.Eq != nil {
return v == *f.Eq
} else if f.Substr != nil {
return strings.Contains(v, *f.Substr)
} else if f.In != nil {
return slices.Contains(f.In, v)
}
return true
}

View file

@ -6,11 +6,26 @@ import (
"time"
"git.kmsign.ru/royalcat/tstor/src/host/controller"
"git.kmsign.ru/royalcat/tstor/src/host/vfs"
"github.com/anacrolix/torrent"
)
type DirEntry interface {
IsDirEntry()
type Dir interface {
IsFsEntry()
IsDir()
GetName() string
GetEntries() []FsEntry
}
type File interface {
IsFsEntry()
IsFile()
GetName() string
GetSize() int64
}
type FsEntry interface {
IsFsEntry()
GetName() string
}
@ -21,12 +36,26 @@ type Progress interface {
}
type ArchiveFs struct {
Name string `json:"name"`
Size int64 `json:"size"`
Name string `json:"name"`
Entries []FsEntry `json:"entries"`
Size int64 `json:"size"`
FS *vfs.ArchiveFS `json:"-"`
}
func (ArchiveFs) IsDirEntry() {}
func (ArchiveFs) IsDir() {}
func (this ArchiveFs) GetName() string { return this.Name }
func (this ArchiveFs) GetEntries() []FsEntry {
if this.Entries == nil {
return nil
}
interfaceSlice := make([]FsEntry, 0, len(this.Entries))
for _, concrete := range this.Entries {
interfaceSlice = append(interfaceSlice, concrete)
}
return interfaceSlice
}
func (ArchiveFs) IsFsEntry() {}
type BooleanFilter struct {
Eq *bool `json:"eq,omitempty"`
@ -45,25 +74,10 @@ type DateTimeFilter struct {
Lte *time.Time `json:"lte,omitempty"`
}
type Dir struct {
Name string `json:"name"`
}
func (Dir) IsDirEntry() {}
func (this Dir) GetName() string { return this.Name }
type DownloadTorrentResponse struct {
Task *Task `json:"task,omitempty"`
}
type File struct {
Name string `json:"name"`
Size int64 `json:"size"`
}
func (File) IsDirEntry() {}
func (this File) GetName() string { return this.Name }
type IntFilter struct {
Eq *int64 `json:"eq,omitempty"`
Gt *int64 `json:"gt,omitempty"`
@ -73,11 +87,6 @@ type IntFilter struct {
In []int64 `json:"in,omitempty"`
}
type ListDirResponse struct {
Root DirEntry `json:"root"`
Entries []DirEntry `json:"entries"`
}
type Mutation struct {
}
@ -90,17 +99,64 @@ type Query struct {
}
type ResolverFs struct {
Name string `json:"name"`
Name string `json:"name"`
Entries []FsEntry `json:"entries"`
FS *vfs.ResolverFS `json:"-"`
}
func (ResolverFs) IsDirEntry() {}
func (ResolverFs) IsDir() {}
func (this ResolverFs) GetName() string { return this.Name }
func (this ResolverFs) GetEntries() []FsEntry {
if this.Entries == nil {
return nil
}
interfaceSlice := make([]FsEntry, 0, len(this.Entries))
for _, concrete := range this.Entries {
interfaceSlice = append(interfaceSlice, concrete)
}
return interfaceSlice
}
func (ResolverFs) IsFsEntry() {}
type Schema struct {
Query *Query `json:"query,omitempty"`
Mutation *Mutation `json:"mutation,omitempty"`
}
type SimpleDir struct {
Name string `json:"name"`
Entries []FsEntry `json:"entries"`
FS vfs.Filesystem `json:"-"`
Path string `json:"-"`
}
func (SimpleDir) IsDir() {}
func (this SimpleDir) GetName() string { return this.Name }
func (this SimpleDir) GetEntries() []FsEntry {
if this.Entries == nil {
return nil
}
interfaceSlice := make([]FsEntry, 0, len(this.Entries))
for _, concrete := range this.Entries {
interfaceSlice = append(interfaceSlice, concrete)
}
return interfaceSlice
}
func (SimpleDir) IsFsEntry() {}
type SimpleFile struct {
Name string `json:"name"`
Size int64 `json:"size"`
}
func (SimpleFile) IsFile() {}
func (this SimpleFile) GetName() string { return this.Name }
func (this SimpleFile) GetSize() int64 { return this.Size }
func (SimpleFile) IsFsEntry() {}
type StringFilter struct {
Eq *string `json:"eq,omitempty"`
Substr *string `json:"substr,omitempty"`
@ -127,12 +183,26 @@ type Torrent struct {
}
type TorrentFs struct {
Name string `json:"name"`
Torrent *Torrent `json:"torrent"`
Name string `json:"name"`
Torrent *Torrent `json:"torrent"`
Entries []FsEntry `json:"entries"`
FS *vfs.TorrentFS `json:"-"`
}
func (TorrentFs) IsDirEntry() {}
func (TorrentFs) IsDir() {}
func (this TorrentFs) GetName() string { return this.Name }
func (this TorrentFs) GetEntries() []FsEntry {
if this.Entries == nil {
return nil
}
interfaceSlice := make([]FsEntry, 0, len(this.Entries))
for _, concrete := range this.Entries {
interfaceSlice = append(interfaceSlice, concrete)
}
return interfaceSlice
}
func (TorrentFs) IsFsEntry() {}
type TorrentFile struct {
Filename string `json:"filename"`
@ -141,6 +211,18 @@ type TorrentFile struct {
F *torrent.File `json:"-"`
}
type TorrentFileEntry struct {
Name string `json:"name"`
Torrent *Torrent `json:"torrent"`
Size int64 `json:"size"`
}
func (TorrentFileEntry) IsFile() {}
func (this TorrentFileEntry) GetName() string { return this.Name }
func (this TorrentFileEntry) GetSize() int64 { return this.Size }
func (TorrentFileEntry) IsFsEntry() {}
type TorrentFilter struct {
Everything *bool `json:"everything,omitempty"`
Infohash *string `json:"infohash,omitempty"`
@ -166,6 +248,7 @@ func (this TorrentProgress) GetCurrent() int64 { return this.Current }
func (this TorrentProgress) GetTotal() int64 { return this.Total }
type TorrentsFilter struct {
Infohash *StringFilter `json:"infohash,omitempty"`
Name *StringFilter `json:"name,omitempty"`
BytesCompleted *IntFilter `json:"bytesCompleted,omitempty"`
BytesMissing *IntFilter `json:"bytesMissing,omitempty"`

View file

@ -0,0 +1,81 @@
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.45
import (
"context"
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model"
)
// Entries is the resolver for the entries field.
func (r *archiveFSResolver) Entries(ctx context.Context, obj *model.ArchiveFs) ([]model.FsEntry, error) {
entries, err := obj.FS.ReadDir(ctx, ".")
if err != nil {
return nil, err
}
out := []model.FsEntry{}
for _, e := range entries {
out = append(out, model.FillFsEntry(e, obj.FS, "."))
}
return out, nil
}
// Entries is the resolver for the entries field.
func (r *resolverFSResolver) Entries(ctx context.Context, obj *model.ResolverFs) ([]model.FsEntry, error) {
entries, err := obj.FS.ReadDir(ctx, ".")
if err != nil {
return nil, err
}
out := []model.FsEntry{}
for _, e := range entries {
out = append(out, model.FillFsEntry(e, obj.FS, "."))
}
return out, nil
}
// Entries is the resolver for the entries field.
func (r *simpleDirResolver) Entries(ctx context.Context, obj *model.SimpleDir) ([]model.FsEntry, error) {
entries, err := obj.FS.ReadDir(ctx, obj.Path)
if err != nil {
return nil, err
}
out := []model.FsEntry{}
for _, e := range entries {
out = append(out, model.FillFsEntry(e, obj.FS, obj.Path))
}
return out, nil
}
// Entries is the resolver for the entries field.
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 {
out = append(out, model.FillFsEntry(e, obj.FS, "."))
}
return out, nil
}
// ArchiveFS returns graph.ArchiveFSResolver implementation.
func (r *Resolver) ArchiveFS() graph.ArchiveFSResolver { return &archiveFSResolver{r} }
// ResolverFS returns graph.ResolverFSResolver implementation.
func (r *Resolver) ResolverFS() graph.ResolverFSResolver { return &resolverFSResolver{r} }
// SimpleDir returns graph.SimpleDirResolver implementation.
func (r *Resolver) SimpleDir() graph.SimpleDirResolver { return &simpleDirResolver{r} }
// TorrentFS returns graph.TorrentFSResolver implementation.
func (r *Resolver) TorrentFS() graph.TorrentFSResolver { return &torrentFSResolver{r} }
type archiveFSResolver struct{ *Resolver }
type resolverFSResolver struct{ *Resolver }
type simpleDirResolver struct{ *Resolver }
type torrentFSResolver struct{ *Resolver }

View file

@ -2,7 +2,7 @@ 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.43
// Code generated by github.com/99designs/gqlgen version v0.17.45
import (
"context"

View file

@ -2,15 +2,16 @@ 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.43
// Code generated by github.com/99designs/gqlgen version v0.17.45
import (
"context"
"io/fs"
"slices"
"strings"
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model"
"git.kmsign.ru/royalcat/tstor/src/host/vfs"
"git.kmsign.ru/royalcat/tstor/src/host/controller"
)
// Torrents is the resolver for the torrents field.
@ -40,6 +41,13 @@ func (r *queryResolver) Torrents(ctx context.Context, filter *model.TorrentsFilt
)
})
}
if filter.Infohash != nil {
filterFuncs = append(filterFuncs, func(torrent *model.Torrent) bool {
return filter.Infohash.Include(
torrent.Infohash,
)
})
}
}
@ -62,78 +70,21 @@ func (r *queryResolver) Torrents(ctx context.Context, filter *model.TorrentsFilt
tr = append(tr, d)
}
slices.SortStableFunc(torrents, func(t1, t2 *controller.Torrent) int {
return strings.Compare(t1.InfoHash(), t2.InfoHash())
})
return tr, nil
}
type dirEntry interface {
Name() string
IsDir() bool
}
func fillDirEntry(e dirEntry) model.DirEntry {
switch e.(type) {
case *vfs.ArchiveFS:
e := e.(*vfs.ArchiveFS)
return model.ArchiveFs{
Name: e.Name(),
Size: e.Size(),
}
case *vfs.ResolverFS:
e := e.(*vfs.ResolverFS)
return model.ResolverFs{
Name: e.Name(),
}
case *vfs.TorrentFs:
e := e.(*vfs.TorrentFs)
return model.TorrentFs{
Name: e.Name(),
Torrent: model.MapTorrent(e.Torrent),
}
default:
if e.IsDir() {
return model.Dir{
Name: e.Name(),
}
}
if de, ok := e.(fs.DirEntry); ok {
info, _ := de.Info()
return model.File{
Name: e.Name(),
Size: info.Size(),
}
}
if fe, ok := e.(fs.FileInfo); ok {
return model.File{
Name: fe.Name(),
Size: fe.Size(),
}
}
}
panic("this dir entry is strange af")
}
// FsListDir is the resolver for the fsListDir field.
func (r *queryResolver) FsListDir(ctx context.Context, path string) (*model.ListDirResponse, error) {
root, err := r.VFS.Stat(ctx, path)
// FsEntry is the resolver for the fsEntry field.
func (r *queryResolver) FsEntry(ctx context.Context, path string) (model.FsEntry, error) {
entry, err := r.VFS.Stat(ctx, path)
if err != nil {
return nil, err
}
entries, err := r.VFS.ReadDir(ctx, path)
if err != nil {
return nil, err
}
out := []model.DirEntry{}
for _, e := range entries {
out = append(out, fillDirEntry(e))
}
return &model.ListDirResponse{
Root: fillDirEntry(root),
Entries: out,
}, nil
return model.FillFsEntry(entry, r.VFS, path), nil
}
// Query returns graph.QueryResolver implementation.

View file

@ -2,7 +2,7 @@ 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.43
// Code generated by github.com/99designs/gqlgen version v0.17.45
import (
"context"

View file

@ -2,7 +2,7 @@ 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.43
// Code generated by github.com/99designs/gqlgen version v0.17.45
import (
"context"

View file

@ -5,84 +5,84 @@ import (
"log/slog"
"net/http"
"git.kmsign.ru/royalcat/tstor"
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
"git.kmsign.ru/royalcat/tstor/src/config"
"git.kmsign.ru/royalcat/tstor/src/host/service"
"git.kmsign.ru/royalcat/tstor/src/host/vfs"
"github.com/anacrolix/missinggo/v2/filecache"
"github.com/gin-contrib/pprof"
"github.com/gin-gonic/gin"
"github.com/shurcooL/httpfs/html/vfstemplate"
echopprof "github.com/labstack/echo-contrib/pprof"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func New(fc *filecache.Cache, ss *service.Stats, s *service.Service, vfs vfs.Filesystem, logPath string, cfg *config.Settings) error {
log := slog.With()
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(gin.Recovery())
r.Use(gin.ErrorLogger())
r.Use(Logger())
pprof.Register(r)
r := echo.New()
r.Use(
middleware.Recover(),
middleware.Gzip(),
middleware.Decompress(),
Logger(),
)
r.GET("/assets/*filepath", func(c *gin.Context) {
c.FileFromFS(c.Request.URL.Path, http.FS(tstor.Assets))
})
echopprof.Register(r)
t, err := vfstemplate.ParseGlob(http.FS(tstor.Templates), nil, "/templates/*")
if err != nil {
return fmt.Errorf("error parsing html: %w", err)
}
// r.GET("/assets/*filepath", func(c *echo.Context) {
// c.FileFromFS(c.Request.URL.Path, http.FS(tstor.Assets))
// })
r.SetHTMLTemplate(t)
// t, err := vfstemplate.ParseGlob(http.FS(tstor.Templates), nil, "/templates/*")
// if err != nil {
// return fmt.Errorf("error parsing html: %w", err)
// }
r.GET("/", indexHandler)
// r.GET("/routes", routesHandler(ss))
r.GET("/logs", logsHandler)
r.GET("/servers", serversFoldersHandler())
r.Any("/graphql", gin.WrapH(GraphQLHandler(s, vfs)))
// r.SetHTMLTemplate(t)
api := r.Group("/api")
{
api.GET("/log", apiLogHandler(logPath))
api.GET("/status", apiStatusHandler(fc, ss))
// api.GET("/servers", apiServersHandler(tss))
// api.GET("/routes", apiRoutesHandler(ss))
// api.POST("/routes/:route/torrent", apiAddTorrentHandler(s))
// api.DELETE("/routes/:route/torrent/:torrent_hash", apiDelTorrentHandler(s))
}
// r.GET("/", indexHandler)
// // r.GET("/routes", routesHandler(ss))
// r.GET("/logs", logsHandler)
// r.GET("/servers", serversFoldersHandler())
r.Any("/graphql", echo.WrapHandler((GraphQLHandler(s, vfs))))
// api := r.Group("/api")
// {
// api.GET("/log", apiLogHandler(logPath))
// api.GET("/status", apiStatusHandler(fc, ss))
// // api.GET("/servers", apiServersHandler(tss))
// // api.GET("/routes", apiRoutesHandler(ss))
// // api.POST("/routes/:route/torrent", apiAddTorrentHandler(s))
// // api.DELETE("/routes/:route/torrent/:torrent_hash", apiDelTorrentHandler(s))
// }
log.Info("starting webserver", "host", fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port))
if err := r.Run(fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port)); err != nil {
return fmt.Errorf("error initializing server: %w", err)
}
// if err := r.Run(fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port)); err != nil {
// return fmt.Errorf("error initializing server: %w", err)
// }
go r.Start((fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port)))
return nil
}
func Logger() gin.HandlerFunc {
l := slog.With("component", "http")
return func(c *gin.Context) {
path := c.Request.URL.Path
raw := c.Request.URL.RawQuery
c.Next()
if raw != "" {
path = path + "?" + raw
}
msg := c.Errors.String()
if msg == "" {
msg = "Request"
}
s := c.Writer.Status()
switch {
case s >= 400 && s < 500:
l.Warn(msg, "path", path, "status", s)
case s >= 500:
l.Error(msg, "path", path, "status", s)
default:
l.Debug(msg, "path", path, "status", s)
}
}
func Logger() echo.MiddlewareFunc {
l := rlog.Component("http")
return middleware.BodyDumpWithConfig(middleware.BodyDumpConfig{
Skipper: func(c echo.Context) bool {
return c.Request().Method == http.MethodGet
},
Handler: func(c echo.Context, reqBody, resBody []byte) {
log := l.With(
slog.String("method", c.Request().Method),
slog.String("uri", c.Request().RequestURI),
)
if c.Request().Header.Get("Content-Type") == "application/json" {
log.Info(c.Request().Context(), "Request body", slog.String("body", string(reqBody)))
}
if c.Response().Header().Get("Content-Type") == "application/json" {
log.Info(c.Request().Context(), "Response body", slog.String("body", string(resBody)))
}
},
})
}

View file

@ -1,8 +1,11 @@
package delivery
import (
"context"
"log/slog"
"net/http"
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/resolver"
"git.kmsign.ru/royalcat/tstor/src/host/service"
@ -27,6 +30,15 @@ func GraphQLHandler(service *service.Service, vfs vfs.Filesystem) http.Handler {
),
)
log := rlog.Component("graphql")
graphqlHandler.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
resp := next(ctx)
responseJson, _ := resp.Data.MarshalJSON()
log.Info(ctx, "response", slog.String("body", string(responseJson)))
return resp
})
graphqlHandler.AddTransport(&transport.POST{})
graphqlHandler.AddTransport(&transport.Websocket{})
graphqlHandler.AddTransport(&transport.SSE{})
@ -39,6 +51,5 @@ func GraphQLHandler(service *service.Service, vfs vfs.Filesystem) http.Handler {
return ctx.Field.Directives.ForName("link") != nil
}),
))
return graphqlHandler
}

View file

@ -36,10 +36,12 @@ func (s *Service) Download(ctx context.Context, task *TorrentDownloadTask) error
}
file.Download()
return nil
} else {
for _, file := range t.Files() {
file.Download()
}
}
t.DownloadAll()
return nil
}
@ -111,15 +113,19 @@ func (s *Service) DownloadProgress(ctx context.Context) (<-chan TorrentProgress,
defer close(out)
for _, t := range torrents {
sub := t.Torrent().SubscribePieceStateChanges()
go func() {
for range sub.Values {
go func(t *controller.Torrent) {
for stateChange := range sub.Values {
if !stateChange.Complete && !stateChange.Partial {
continue
}
out <- TorrentProgress{
Torrent: t,
Current: t.BytesCompleted(),
Total: t.Length(),
}
}
}()
}(t)
defer sub.Close()
}

View file

@ -342,7 +342,7 @@ func getFile[F File](m map[string]F, name string) (File, error) {
func listDirFromFiles[F File](m map[string]F, name string) ([]fs.DirEntry, error) {
out := make([]fs.DirEntry, 0, len(m))
name = AddTrailSlash(name)
name = AddTrailSlash(path.Clean(name))
for p, f := range m {
if strings.HasPrefix(p, name) {
parts := strings.Split(trimRelPath(p, name), Separator)

View file

@ -19,7 +19,7 @@ import (
"golang.org/x/exp/maps"
)
type TorrentFs struct {
type TorrentFS struct {
name string
mu sync.Mutex
@ -32,64 +32,64 @@ type TorrentFs struct {
resolver *resolver
}
var _ Filesystem = (*TorrentFs)(nil)
var _ Filesystem = (*TorrentFS)(nil)
func NewTorrentFs(name string, c *controller.Torrent) *TorrentFs {
return &TorrentFs{
func NewTorrentFs(name string, c *controller.Torrent) *TorrentFS {
return &TorrentFS{
name: name,
Torrent: c,
resolver: newResolver(ArchiveFactories),
}
}
var _ fs.DirEntry = (*TorrentFs)(nil)
var _ fs.DirEntry = (*TorrentFS)(nil)
// Name implements fs.DirEntry.
func (tfs *TorrentFs) Name() string {
func (tfs *TorrentFS) Name() string {
return tfs.name
}
// Info implements fs.DirEntry.
func (tfs *TorrentFs) Info() (fs.FileInfo, error) {
func (tfs *TorrentFS) Info() (fs.FileInfo, error) {
return tfs, nil
}
// IsDir implements fs.DirEntry.
func (tfs *TorrentFs) IsDir() bool {
func (tfs *TorrentFS) IsDir() bool {
return true
}
// Type implements fs.DirEntry.
func (tfs *TorrentFs) Type() fs.FileMode {
func (tfs *TorrentFS) Type() fs.FileMode {
return fs.ModeDir
}
// ModTime implements fs.FileInfo.
func (tfs *TorrentFs) ModTime() time.Time {
func (tfs *TorrentFS) ModTime() time.Time {
return time.Time{}
}
// Mode implements fs.FileInfo.
func (tfs *TorrentFs) Mode() fs.FileMode {
func (tfs *TorrentFS) Mode() fs.FileMode {
return fs.ModeDir
}
// Size implements fs.FileInfo.
func (tfs *TorrentFs) Size() int64 {
func (tfs *TorrentFS) Size() int64 {
return 0
}
// Sys implements fs.FileInfo.
func (tfs *TorrentFs) Sys() any {
func (tfs *TorrentFS) Sys() any {
return nil
}
// FsName implements Filesystem.
func (tfs *TorrentFs) FsName() string {
func (tfs *TorrentFS) FsName() string {
return "torrentfs"
}
func (fs *TorrentFs) files(ctx context.Context) (map[string]File, error) {
func (fs *TorrentFS) files(ctx context.Context) (map[string]File, error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -175,7 +175,7 @@ DEFAULT_DIR:
// return true
// }
func (fs *TorrentFs) listFilesRecursive(ctx context.Context, vfs Filesystem, start string) (map[string]File, error) {
func (fs *TorrentFS) listFilesRecursive(ctx context.Context, vfs Filesystem, start string) (map[string]File, error) {
ctx, span := tracer.Start(ctx, "listFilesRecursive",
fs.traceAttrs(attribute.String("start", start)),
)
@ -206,7 +206,7 @@ func (fs *TorrentFs) listFilesRecursive(ctx context.Context, vfs Filesystem, sta
return out, nil
}
func (fs *TorrentFs) rawOpen(ctx context.Context, filename string) (file File, err error) {
func (fs *TorrentFS) rawOpen(ctx context.Context, filename string) (file File, err error) {
ctx, span := tracer.Start(ctx, "rawOpen",
fs.traceAttrs(attribute.String("filename", filename)),
)
@ -225,7 +225,7 @@ func (fs *TorrentFs) rawOpen(ctx context.Context, filename string) (file File, e
return file, err
}
func (fs *TorrentFs) rawStat(ctx context.Context, filename string) (fs.FileInfo, error) {
func (fs *TorrentFS) rawStat(ctx context.Context, filename string) (fs.FileInfo, error) {
ctx, span := tracer.Start(ctx, "rawStat",
fs.traceAttrs(attribute.String("filename", filename)),
)
@ -243,7 +243,7 @@ func (fs *TorrentFs) rawStat(ctx context.Context, filename string) (fs.FileInfo,
return file.Info()
}
func (fs *TorrentFs) traceAttrs(add ...attribute.KeyValue) trace.SpanStartOption {
func (fs *TorrentFS) traceAttrs(add ...attribute.KeyValue) trace.SpanStartOption {
return trace.WithAttributes(append([]attribute.KeyValue{
attribute.String("fs", fs.FsName()),
attribute.String("torrent", fs.Torrent.Name()),
@ -252,7 +252,7 @@ func (fs *TorrentFs) traceAttrs(add ...attribute.KeyValue) trace.SpanStartOption
}
// Stat implements Filesystem.
func (tfs *TorrentFs) Stat(ctx context.Context, filename string) (fs.FileInfo, error) {
func (tfs *TorrentFS) Stat(ctx context.Context, filename string) (fs.FileInfo, error) {
ctx, span := tracer.Start(ctx, "Stat",
tfs.traceAttrs(attribute.String("filename", filename)),
)
@ -287,7 +287,7 @@ func (tfs *TorrentFs) Stat(ctx context.Context, filename string) (fs.FileInfo, e
return tfs.rawStat(ctx, fsPath)
}
func (tfs *TorrentFs) Open(ctx context.Context, filename string) (file File, err error) {
func (tfs *TorrentFS) Open(ctx context.Context, filename string) (file File, err error) {
ctx, span := tracer.Start(ctx, "Open",
tfs.traceAttrs(attribute.String("filename", filename)),
)
@ -322,7 +322,7 @@ func (tfs *TorrentFs) Open(ctx context.Context, filename string) (file File, err
return tfs.rawOpen(ctx, fsPath)
}
func (tfs *TorrentFs) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, error) {
func (tfs *TorrentFS) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, error) {
ctx, span := tracer.Start(ctx, "ReadDir",
tfs.traceAttrs(attribute.String("name", name)),
)
@ -357,7 +357,7 @@ func (tfs *TorrentFs) ReadDir(ctx context.Context, name string) ([]fs.DirEntry,
return listDirFromFiles(files, fsPath)
}
func (fs *TorrentFs) Unlink(ctx context.Context, name string) error {
func (fs *TorrentFS) Unlink(ctx context.Context, name string) error {
ctx, span := tracer.Start(ctx, "Unlink",
fs.traceAttrs(attribute.String("name", name)),
)