package mega | |
import ( | |
"context" | |
"fmt" | |
"github.com/alist-org/alist/v3/pkg/utils" | |
"github.com/t3rm1n4l/go-mega" | |
"io" | |
"sync" | |
"time" | |
) | |
// do others that not defined in Driver interface | |
// openObject represents a download in progress | |
type openObject struct { | |
ctx context.Context | |
mu sync.Mutex | |
d *mega.Download | |
id int | |
skip int64 | |
chunk []byte | |
closed bool | |
} | |
// get the next chunk | |
func (oo *openObject) getChunk(ctx context.Context) (err error) { | |
if oo.id >= oo.d.Chunks() { | |
return io.EOF | |
} | |
var chunk []byte | |
err = utils.Retry(3, time.Second, func() (err error) { | |
chunk, err = oo.d.DownloadChunk(oo.id) | |
return err | |
}) | |
if err != nil { | |
return err | |
} | |
oo.id++ | |
oo.chunk = chunk | |
return nil | |
} | |
// Read reads up to len(p) bytes into p. | |
func (oo *openObject) Read(p []byte) (n int, err error) { | |
oo.mu.Lock() | |
defer oo.mu.Unlock() | |
if oo.closed { | |
return 0, fmt.Errorf("read on closed file") | |
} | |
// Skip data at the start if requested | |
for oo.skip > 0 { | |
_, size, err := oo.d.ChunkLocation(oo.id) | |
if err != nil { | |
return 0, err | |
} | |
if oo.skip < int64(size) { | |
break | |
} | |
oo.id++ | |
oo.skip -= int64(size) | |
} | |
if len(oo.chunk) == 0 { | |
err = oo.getChunk(oo.ctx) | |
if err != nil { | |
return 0, err | |
} | |
if oo.skip > 0 { | |
oo.chunk = oo.chunk[oo.skip:] | |
oo.skip = 0 | |
} | |
} | |
n = copy(p, oo.chunk) | |
oo.chunk = oo.chunk[n:] | |
return n, nil | |
} | |
// Close closed the file - MAC errors are reported here | |
func (oo *openObject) Close() (err error) { | |
oo.mu.Lock() | |
defer oo.mu.Unlock() | |
if oo.closed { | |
return nil | |
} | |
err = utils.Retry(3, 500*time.Millisecond, func() (err error) { | |
return oo.d.Finish() | |
}) | |
if err != nil { | |
return fmt.Errorf("failed to finish download: %w", err) | |
} | |
oo.closed = true | |
return nil | |
} | |