tstor/src/sources/qbittorrent/fs.go

229 lines
4.3 KiB
Go
Raw Normal View History

package qbittorrent
import (
"context"
"io"
"io/fs"
"os"
"path"
"time"
"git.kmsign.ru/royalcat/tstor/src/vfs"
)
type FS struct {
client *client
name string
hash string
dataDir string
}
var _ vfs.Filesystem = (*FS)(nil)
func newTorrentFS(client *client, name string, hash string, dataDir string) (*FS, error) {
return &FS{
client: client,
name: name,
hash: hash,
dataDir: dataDir,
}, nil
}
// Info implements vfs.Filesystem.
func (f *FS) Info() (fs.FileInfo, error) {
return vfs.NewDirInfo(f.name), nil
}
// IsDir implements vfs.Filesystem.
func (f *FS) IsDir() bool {
return true
}
// Name implements vfs.Filesystem.
func (f *FS) Name() string {
return path.Base(f.dataDir)
}
// Open implements vfs.Filesystem.
func (f *FS) Open(ctx context.Context, filename string) (vfs.File, error) {
panic("unimplemented")
}
// ReadDir implements vfs.Filesystem.
func (f *FS) ReadDir(ctx context.Context, path string) ([]fs.DirEntry, error) {
panic("unimplemented")
}
// Stat implements vfs.Filesystem.
func (f *FS) Stat(ctx context.Context, filename string) (fs.FileInfo, error) {
return vfs.NewDirInfo(f.name), nil
}
// Type implements vfs.Filesystem.
func (f *FS) Type() fs.FileMode {
return vfs.ROMode
}
// Unlink implements vfs.Filesystem.
func (f *FS) Unlink(ctx context.Context, filename string) error {
panic("unimplemented")
}
func openFile(ctx context.Context, client client, hash, filePath string) *File {
client.getFileContent(ctx, hash, 0)
return &File{
client: client,
hash: hash,
filePath: filePath,
}
}
type File struct {
client client
hash string
dataDir string
filePath string // path inside a torrent directory
contentIndex int
pieceSize int
fileSize int64
offset int64
osfile *os.File
}
var _ vfs.File = (*File)(nil)
// Close implements vfs.File.
func (f *File) Close(ctx context.Context) error {
if f.osfile != nil {
err := f.osfile.Close()
f.osfile = nil
return err
}
return nil
}
// Info implements vfs.File.
func (f *File) Info() (fs.FileInfo, error) {
return &fileInfo{name: path.Base(f.filePath), size: f.fileSize}, nil
}
// IsDir implements vfs.File.
func (f *File) IsDir() bool {
return false
}
// Seek implements vfs.File.
func (f *File) Seek(offset int64, whence int) (int64, error) {
switch whence {
case io.SeekStart:
f.offset = offset
case io.SeekCurrent:
f.offset += offset
case io.SeekEnd:
f.offset = f.fileSize + offset
}
return f.offset, nil
}
// Name implements vfs.File.
func (f *File) Name() string {
return path.Base(f.filePath)
}
// Read implements vfs.File.
func (f *File) Read(ctx context.Context, p []byte) (n int, err error) {
pieceIndex := int(f.offset / int64(f.pieceSize))
err = f.client.waitPieceToComplete(ctx, f.hash, pieceIndex)
if err != nil {
return 0, err
}
descriptor, err := f.descriptor()
if err != nil {
return 0, err
}
n, err = descriptor.ReadAt(p, f.offset)
f.offset += int64(n)
return n, err
}
// ReadAt implements vfs.File.
func (f *File) ReadAt(ctx context.Context, p []byte, off int64) (n int, err error) {
pieceIndex := int(off / int64(f.pieceSize))
err = f.client.waitPieceToComplete(ctx, f.hash, pieceIndex)
if err != nil {
return 0, err
}
descriptor, err := f.descriptor()
if err != nil {
return 0, err
}
return descriptor.ReadAt(p, off)
}
// Size implements vfs.File.
func (f *File) Size() int64 {
return f.fileSize
}
// Type implements vfs.File.
func (f *File) Type() fs.FileMode {
return vfs.ROMode
}
func (f *File) descriptor() (*os.File, error) {
if f.osfile != nil {
return f.osfile, nil
}
osfile, err := os.Open(path.Join(f.dataDir, f.filePath))
if err != nil {
return nil, err
}
f.osfile = osfile
return f.osfile, nil
}
type fileInfo struct {
name string
size int64
}
var _ fs.FileInfo = (*fileInfo)(nil)
// IsDir implements fs.FileInfo.
func (f *fileInfo) IsDir() bool {
return false
}
// ModTime implements fs.FileInfo.
func (f *fileInfo) ModTime() time.Time {
return time.Time{}
}
// Mode implements fs.FileInfo.
func (f *fileInfo) Mode() fs.FileMode {
return vfs.ROMode
}
// Name implements fs.FileInfo.
func (f *fileInfo) Name() string {
return f.name
}
// Size implements fs.FileInfo.
func (f *fileInfo) Size() int64 {
return f.size
}
// Sys implements fs.FileInfo.
func (f *fileInfo) Sys() any {
return nil
}