From b91aa1db386dd5eaf447ab41c5e13921af419dd2 Mon Sep 17 00:00:00 2001 From: Mukhtar Akere Date: Sat, 15 Mar 2025 23:12:37 +0100 Subject: [PATCH] Add a precacher to significantly improve importing to arrs/plex --- doc/config.full.json | 1 + internal/config/config.go | 1 + pkg/qbit/downloader.go | 53 +++++++++++++++++++++++++++++++++++++++ pkg/qbit/qbit.go | 2 ++ 4 files changed, 57 insertions(+) diff --git a/doc/config.full.json b/doc/config.full.json index b3107e5..dda1890 100644 --- a/doc/config.full.json +++ b/doc/config.full.json @@ -51,6 +51,7 @@ "download_folder": "/mnt/symlinks/", "categories": ["sonarr", "radarr"], "refresh_interval": 5, + "skip_pre_cache": false }, "arrs": [ { diff --git a/internal/config/config.go b/internal/config/config.go index 70f4372..bd9b816 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -41,6 +41,7 @@ type QBitTorrent struct { DownloadFolder string `json:"download_folder"` Categories []string `json:"categories"` RefreshInterval int `json:"refresh_interval"` + SkipPreCache bool `json:"skip_pre_cache"` } type Arr struct { diff --git a/pkg/qbit/downloader.go b/pkg/qbit/downloader.go index c9ae894..d2ecb52 100644 --- a/pkg/qbit/downloader.go +++ b/pkg/qbit/downloader.go @@ -6,6 +6,7 @@ import ( "github.com/cavaliergopher/grab/v3" "github.com/sirrobot01/debrid-blackhole/internal/utils" debrid "github.com/sirrobot01/debrid-blackhole/pkg/debrid/torrent" + "io" "net/http" "os" "path/filepath" @@ -202,4 +203,56 @@ func (q *QBit) createSymLink(path string, torrentMountPath string, file debrid.F // It's okay if the symlink already exists q.logger.Debug().Msgf("Failed to create symlink: %s: %v", fullPath, err) } + if q.SkipPreCache { + return + } + go func() { + err := q.preCacheFile(torrentFilePath) + if err != nil { + q.logger.Debug().Msgf("Failed to pre-cache file: %s: %v", torrentFilePath, err) + } + }() +} + +func (q *QBit) preCacheFile(filePath string) error { + q.logger.Trace().Msgf("Pre-caching file: %s", filePath) + file, err := os.Open(filePath) + if err != nil { + return fmt.Errorf("error opening file: %v", err) + } + defer file.Close() + + // Pre-cache the file header (first 256KB) using 16KB chunks. + q.readSmallChunks(file, 0, 256*1024, 16*1024) + q.readSmallChunks(file, 1024*1024, 64*1024, 16*1024) + + return nil +} + +func (q *QBit) readSmallChunks(file *os.File, startPos int64, totalToRead int, chunkSize int) { + _, err := file.Seek(startPos, 0) + if err != nil { + return + } + + buf := make([]byte, chunkSize) + bytesRemaining := totalToRead + + for bytesRemaining > 0 { + toRead := chunkSize + if bytesRemaining < chunkSize { + toRead = bytesRemaining + } + + n, err := file.Read(buf[:toRead]) + if err != nil { + if err == io.EOF { + break + } + return + } + + bytesRemaining -= n + } + return } diff --git a/pkg/qbit/qbit.go b/pkg/qbit/qbit.go index c7102dd..84726ea 100644 --- a/pkg/qbit/qbit.go +++ b/pkg/qbit/qbit.go @@ -19,6 +19,7 @@ type QBit struct { logger zerolog.Logger Tags []string RefreshInterval int + SkipPreCache bool } func New() *QBit { @@ -35,5 +36,6 @@ func New() *QBit { Storage: NewTorrentStorage(filepath.Join(_cfg.Path, "torrents.json")), logger: logger.NewLogger("qbit", _cfg.LogLevel, os.Stdout), RefreshInterval: refreshInterval, + SkipPreCache: cfg.SkipPreCache, } }