161 lines
3.2 KiB
Go
161 lines
3.2 KiB
Go
package webdav
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/sirrobot01/debrid-blackhole/pkg/debrid/cache"
|
|
"github.com/sirrobot01/debrid-blackhole/pkg/debrid/torrent"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
type File struct {
|
|
cache *cache.Cache
|
|
cachedTorrent *cache.CachedTorrent
|
|
file *torrent.File
|
|
offset int64
|
|
isDir bool
|
|
children []os.FileInfo
|
|
reader io.ReadCloser
|
|
}
|
|
|
|
// File interface implementations for File
|
|
|
|
func (f *File) Close() error {
|
|
return nil
|
|
}
|
|
|
|
func (f *File) GetDownloadLink() string {
|
|
file := f.file
|
|
link, err := f.cache.GetFileDownloadLink(f.cachedTorrent, file)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return link
|
|
}
|
|
|
|
func (f *File) Read(p []byte) (n int, err error) {
|
|
// Directories cannot be read as a byte stream.
|
|
if f.isDir {
|
|
return 0, os.ErrInvalid
|
|
}
|
|
|
|
// If we haven't started streaming the file yet, open the HTTP connection.
|
|
if f.reader == nil {
|
|
// Create an HTTP GET request to the file's URL.
|
|
req, err := http.NewRequest("GET", f.GetDownloadLink(), nil)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to create HTTP request: %w", err)
|
|
}
|
|
|
|
// If we've already read some data (f.offset > 0), request only the remaining bytes.
|
|
if f.offset > 0 {
|
|
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", f.offset))
|
|
}
|
|
|
|
// Execute the HTTP request.
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("HTTP request error: %w", err)
|
|
}
|
|
|
|
// Accept a 200 (OK) or 206 (Partial Content) status.
|
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
|
|
resp.Body.Close()
|
|
return 0, fmt.Errorf("unexpected HTTP status: %d", resp.StatusCode)
|
|
}
|
|
|
|
// Store the response body as our reader.
|
|
f.reader = resp.Body
|
|
}
|
|
|
|
// Read data from the HTTP stream.
|
|
n, err = f.reader.Read(p)
|
|
f.offset += int64(n)
|
|
|
|
// When we reach the end of the stream, close the reader.
|
|
if err == io.EOF {
|
|
f.reader.Close()
|
|
f.reader = nil
|
|
}
|
|
|
|
return n, err
|
|
}
|
|
|
|
func (f *File) Seek(offset int64, whence int) (int64, error) {
|
|
if f.isDir {
|
|
return 0, os.ErrInvalid
|
|
}
|
|
|
|
switch whence {
|
|
case io.SeekStart:
|
|
f.offset = offset
|
|
case io.SeekCurrent:
|
|
f.offset += offset
|
|
case io.SeekEnd:
|
|
f.offset = f.file.Size - offset
|
|
default:
|
|
return 0, os.ErrInvalid
|
|
}
|
|
|
|
if f.offset < 0 {
|
|
f.offset = 0
|
|
}
|
|
if f.offset > f.file.Size {
|
|
f.offset = f.file.Size
|
|
}
|
|
|
|
return f.offset, nil
|
|
}
|
|
|
|
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
|
|
if !f.isDir {
|
|
return nil, os.ErrInvalid
|
|
}
|
|
|
|
if count <= 0 {
|
|
return f.children, nil
|
|
}
|
|
|
|
if len(f.children) == 0 {
|
|
return nil, io.EOF
|
|
}
|
|
|
|
if count > len(f.children) {
|
|
count = len(f.children)
|
|
}
|
|
|
|
files := f.children[:count]
|
|
f.children = f.children[count:]
|
|
return files, nil
|
|
}
|
|
|
|
func (f *File) Stat() (os.FileInfo, error) {
|
|
if f.isDir {
|
|
name := "/"
|
|
if f.cachedTorrent != nil {
|
|
name = f.cachedTorrent.Name
|
|
}
|
|
return &FileInfo{
|
|
name: name,
|
|
size: 0,
|
|
mode: 0755 | os.ModeDir,
|
|
modTime: time.Now(),
|
|
isDir: true,
|
|
}, nil
|
|
}
|
|
|
|
return &FileInfo{
|
|
name: f.file.Name,
|
|
size: f.file.Size,
|
|
mode: 0644,
|
|
modTime: time.Now(),
|
|
isDir: false,
|
|
}, nil
|
|
}
|
|
|
|
func (f *File) Write(p []byte) (n int, err error) {
|
|
return 0, os.ErrPermission
|
|
}
|