package zfs import ( "context" "fmt" "os" "path/filepath" "syscall" "time" "bazil.org/fuse" "bazil.org/fuse/fs" "github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/internal/universal" ) // define variable as rootObject id const ( ROOT = 0 DIRECTORY = 1 TORRENT = 2 FILE = 3 ) type Object struct { fs *FS objType int parentName string name string torrent *torrent.Torrent file *torrent.File size uint64 mtime time.Time } // Attr returns the attributes for a directory func (o Object) Attr(ctx context.Context, attr *fuse.Attr) error { if o.objType == FILE { attr.Mode = 0644 } else { attr.Mode = os.ModeDir | 0755 } attr.Size = o.size attr.Uid = o.fs.uid attr.Gid = o.fs.gid attr.Ctime = o.mtime attr.Mtime = o.mtime attr.Blocks = (attr.Size + 511) >> 9 return nil } // ReadDirAll shows all files in the current directory func (o Object) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { dirs := []fuse.Dirent{} switch o.objType { case ROOT: for _, directory := range o.fs.config.GetDirectories() { dirs = append(dirs, fuse.Dirent{ Name: directory, Type: fuse.DT_Dir, }) } case DIRECTORY: for el := o.fs.tMgr.TorrentMap.Front(); el != nil; el = el.Next() { torrent := el.Value if torrent.InProgress { continue } dirs = append(dirs, fuse.Dirent{ Name: torrent.AccessKey, Type: fuse.DT_Dir, }) } case TORRENT: torrent, _ := o.fs.tMgr.TorrentMap.Get(o.name) if torrent == nil || torrent.InProgress { return nil, syscall.ENOENT } for el := torrent.SelectedFiles.Front(); el != nil; el = el.Next() { file := el.Value if file.Link == "" { // log.Println("File has no link, skipping", file.Path) continue } filename := filepath.Base(file.Path) dirs = append(dirs, fuse.Dirent{ Name: filename, Type: fuse.DT_File, }) } } return dirs, nil } // Lookup tests if a file is existent in the current directory func (o Object) Lookup(ctx context.Context, name string) (fs.Node, error) { switch o.objType { case ROOT: for _, directory := range o.fs.config.GetDirectories() { if directory == name { return Object{ fs: o.fs, objType: DIRECTORY, parentName: o.name, name: name, mtime: o.fs.initTime, }, nil } } case DIRECTORY: torrent, _ := o.fs.tMgr.TorrentMap.Get(name) if torrent == nil { return nil, syscall.ENOENT } return Object{ fs: o.fs, objType: TORRENT, parentName: o.name, name: name, mtime: convertRFC3339toTime(torrent.LatestAdded), }, nil case TORRENT: torrent, _ := o.fs.tMgr.TorrentMap.Get(o.name) if torrent == nil { return nil, syscall.ENOENT } file, _ := torrent.SelectedFiles.Get(name) if file == nil { return nil, syscall.ENOENT } return Object{ fs: o.fs, objType: FILE, parentName: o.name, name: name, torrent: torrent, file: file, size: uint64(file.Bytes), mtime: convertRFC3339toTime(file.Added), }, nil } return nil, syscall.ENOENT } // Open a file func (o Object) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { resp.Flags |= fuse.OpenDirectIO return o, nil } // Read reads some bytes or the whole file func (o Object) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { reader := universal.GetFileReader(o.torrent, o.file, req.Offset, int(req.Size), o.fs.tMgr, o.fs.config, o.fs.log) if reader == nil { return syscall.EIO } defer reader.Close() resp.Data = make([]byte, req.Size) _, err := reader.Read(resp.Data) if err != nil && err.Error() != "EOF" { o.fs.log.Errorf("Error reading bytes from Real-Debrid: %v", err) return syscall.EIO } return nil } // Remove deletes an element func (o Object) Remove(ctx context.Context, req *fuse.RemoveRequest) error { return fmt.Errorf("Remove not yet implemented") } // Rename renames an element func (o Object) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error { return fmt.Errorf("Rename not yet implemented") } func convertRFC3339toTime(input string) time.Time { t, err := time.Parse(time.RFC3339, input) if err != nil { return time.Now() } return t }