2024-08-22 22:16:16 +00:00
|
|
|
package qbittorrent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"slices"
|
|
|
|
"time"
|
|
|
|
|
2024-08-31 23:00:13 +00:00
|
|
|
"git.kmsign.ru/royalcat/tstor/pkg/qbittorrent"
|
2024-08-22 22:16:16 +00:00
|
|
|
)
|
|
|
|
|
2024-08-31 23:00:13 +00:00
|
|
|
type cacheClient struct {
|
2024-08-22 22:16:16 +00:00
|
|
|
qb qbittorrent.Client
|
|
|
|
}
|
|
|
|
|
2024-08-31 23:00:13 +00:00
|
|
|
func wrapClient(qb qbittorrent.Client) *cacheClient {
|
|
|
|
return &cacheClient{qb: qb}
|
2024-08-22 22:16:16 +00:00
|
|
|
}
|
|
|
|
|
2024-08-31 23:00:13 +00:00
|
|
|
var errNotFound = fmt.Errorf("not found")
|
|
|
|
|
|
|
|
func (f *cacheClient) getProperties(ctx context.Context, hash string) (*qbittorrent.TorrentProperties, error) {
|
|
|
|
info, err := f.qb.Torrent().GetProperties(ctx, hash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return info, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *cacheClient) listContent(ctx context.Context, hash string) ([]*qbittorrent.TorrentContent, error) {
|
|
|
|
contents, err := f.qb.Torrent().GetContents(ctx, hash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return contents, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *cacheClient) getContent(ctx context.Context, hash string, contentIndex int) (*qbittorrent.TorrentContent, error) {
|
|
|
|
contents, err := f.qb.Torrent().GetContents(ctx, hash, contentIndex)
|
2024-08-22 22:16:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-08-31 23:00:13 +00:00
|
|
|
contentI := slices.IndexFunc(contents, func(c *qbittorrent.TorrentContent) bool {
|
|
|
|
return c.Index == contentIndex
|
2024-08-22 22:16:16 +00:00
|
|
|
})
|
2024-08-31 23:00:13 +00:00
|
|
|
if contentI == -1 {
|
2024-08-22 22:16:16 +00:00
|
|
|
return nil, fmt.Errorf("content not found")
|
|
|
|
}
|
|
|
|
|
2024-08-31 23:00:13 +00:00
|
|
|
return contents[contentI], nil
|
2024-08-22 22:16:16 +00:00
|
|
|
}
|
|
|
|
|
2024-08-31 23:00:13 +00:00
|
|
|
func (f *cacheClient) isPieceComplete(ctx context.Context, hash string, pieceIndex int) (bool, error) {
|
|
|
|
completion, err := f.qb.Torrent().GetPiecesStates(ctx, hash)
|
2024-08-22 22:16:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if completion[pieceIndex] == 2 {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2024-08-31 23:00:13 +00:00
|
|
|
func (f *cacheClient) waitPieceToComplete(ctx context.Context, hash string, pieceIndex int) error {
|
2024-08-22 22:16:16 +00:00
|
|
|
const checkingInterval = 1 * time.Second
|
|
|
|
|
|
|
|
ok, err := f.isPieceComplete(ctx, hash, pieceIndex)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if deadline, ok := ctx.Deadline(); ok && time.Until(deadline) < checkingInterval {
|
|
|
|
return context.DeadlineExceeded
|
|
|
|
}
|
|
|
|
|
|
|
|
ticker := time.NewTicker(checkingInterval)
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
case <-ticker.C:
|
|
|
|
ok, err := f.isPieceComplete(ctx, hash, pieceIndex)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if deadline, ok := ctx.Deadline(); ok && time.Until(deadline) < checkingInterval {
|
|
|
|
return context.DeadlineExceeded
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|