Add more rclone flags, fix minor issues
This commit is contained in:
@@ -7,10 +7,10 @@ import (
|
||||
"github.com/sirrobot01/decypharr/internal/logger"
|
||||
"github.com/sirrobot01/decypharr/pkg/qbit"
|
||||
"github.com/sirrobot01/decypharr/pkg/server"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/version"
|
||||
"github.com/sirrobot01/decypharr/pkg/web"
|
||||
"github.com/sirrobot01/decypharr/pkg/webdav"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -77,7 +77,7 @@ func Start(ctx context.Context) error {
|
||||
reset := func() {
|
||||
// Reset the store and services
|
||||
qb.Reset()
|
||||
store.Reset()
|
||||
wire.Reset()
|
||||
// refresh GC
|
||||
runtime.GC()
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func startServices(ctx context.Context, cancelSvc context.CancelFunc, wd *webdav
|
||||
|
||||
// Start rclone RC server if enabled
|
||||
safeGo(func() error {
|
||||
rcManager := store.Get().RcloneManager()
|
||||
rcManager := wire.Get().RcloneManager()
|
||||
if rcManager == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func startServices(ctx context.Context, cancelSvc context.CancelFunc, wd *webdav
|
||||
|
||||
if cfg := config.Get(); cfg.Repair.Enabled {
|
||||
safeGo(func() error {
|
||||
repair := store.Get().Repair()
|
||||
repair := wire.Get().Repair()
|
||||
if repair != nil {
|
||||
if err := repair.Start(ctx); err != nil {
|
||||
_log.Error().Err(err).Msg("repair failed")
|
||||
@@ -171,7 +171,7 @@ func startServices(ctx context.Context, cancelSvc context.CancelFunc, wd *webdav
|
||||
}
|
||||
|
||||
safeGo(func() error {
|
||||
store.Get().StartWorkers(ctx)
|
||||
wire.Get().StartWorkers(ctx)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ type Rclone struct {
|
||||
// Global mount folder where all providers will be mounted as subfolders
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
MountPath string `json:"mount_path,omitempty"`
|
||||
RcPort string `json:"rc_port,omitempty"`
|
||||
|
||||
// Cache settings
|
||||
CacheDir string `json:"cache_dir,omitempty"`
|
||||
@@ -98,6 +99,7 @@ type Rclone struct {
|
||||
// VFS settings
|
||||
VfsCacheMode string `json:"vfs_cache_mode,omitempty"` // off, minimal, writes, full
|
||||
VfsCacheMaxAge string `json:"vfs_cache_max_age,omitempty"` // Maximum age of objects in the cache (default 1h)
|
||||
VfsDiskSpaceTotal string `json:"vfs_disk_space_total,omitempty"` // Total disk space available for the cache (default off)
|
||||
VfsCacheMaxSize string `json:"vfs_cache_max_size,omitempty"` // Maximum size of the cache (default off)
|
||||
VfsCachePollInterval string `json:"vfs_cache_poll_interval,omitempty"` // How often to poll for changes (default 1m)
|
||||
VfsReadChunkSize string `json:"vfs_read_chunk_size,omitempty"` // Read chunk size (default 128M)
|
||||
@@ -106,6 +108,13 @@ type Rclone struct {
|
||||
VfsPollInterval string `json:"vfs_poll_interval,omitempty"` // How often to rclone cleans the cache (default 1m)
|
||||
BufferSize string `json:"buffer_size,omitempty"` // Buffer size for reading files (default 16M)
|
||||
|
||||
VfsCacheMinFreeSpace string `json:"vfs_cache_min_free_space,omitempty"`
|
||||
VfsFastFingerprint bool `json:"vfs_fast_fingerprint,omitempty"`
|
||||
VfsReadChunkStreams int `json:"vfs_read_chunk_streams,omitempty"`
|
||||
AsyncRead *bool `json:"async_read,omitempty"` // Use async read for files
|
||||
Transfers int `json:"transfers,omitempty"` // Number of transfers to use (default 4)
|
||||
UseMmap bool `json:"use_mmap,omitempty"`
|
||||
|
||||
// File system settings
|
||||
UID uint32 `json:"uid,omitempty"` // User ID for mounted files
|
||||
GID uint32 `json:"gid,omitempty"` // Group ID for mounted files
|
||||
@@ -417,6 +426,11 @@ func (c *Config) setDefaults() {
|
||||
|
||||
// Rclone defaults
|
||||
if c.Rclone.Enabled {
|
||||
c.Rclone.RcPort = cmp.Or(c.Rclone.RcPort, "5572")
|
||||
if c.Rclone.AsyncRead == nil {
|
||||
_asyncTrue := true
|
||||
c.Rclone.AsyncRead = &_asyncTrue
|
||||
}
|
||||
c.Rclone.VfsCacheMode = cmp.Or(c.Rclone.VfsCacheMode, "off")
|
||||
if c.Rclone.UID == 0 {
|
||||
c.Rclone.UID = uint32(os.Getuid())
|
||||
@@ -429,6 +443,9 @@ func (c *Config) setDefaults() {
|
||||
c.Rclone.GID = uint32(os.Getgid())
|
||||
}
|
||||
}
|
||||
if c.Rclone.Transfers == 0 {
|
||||
c.Rclone.Transfers = 4 // Default number of transfers
|
||||
}
|
||||
if c.Rclone.VfsCacheMode != "off" {
|
||||
c.Rclone.VfsCachePollInterval = cmp.Or(c.Rclone.VfsCachePollInterval, "1m") // Clean cache every minute
|
||||
}
|
||||
|
||||
@@ -958,8 +958,7 @@ func (r *RealDebrid) GetProfile() (*types.Profile, error) {
|
||||
}
|
||||
|
||||
func (r *RealDebrid) GetAvailableSlots() (int, error) {
|
||||
url := fmt.Sprintf("%s/torrents/activeCount", r.Host)
|
||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/torrents/activeCount", r.Host), nil)
|
||||
resp, err := r.client.MakeRequest(req)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/sirrobot01/decypharr/internal/config"
|
||||
"github.com/sirrobot01/decypharr/pkg/arr"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -130,7 +130,7 @@ func (q *QBit) authContext(next http.Handler) http.Handler {
|
||||
cfg := config.Get()
|
||||
host, token, err := decodeAuthHeader(r.Header.Get("Authorization"))
|
||||
category := getCategory(r.Context())
|
||||
arrs := store.Get().Arr()
|
||||
arrs := wire.Get().Arr()
|
||||
// Check if arr exists
|
||||
a := arrs.Get(category)
|
||||
if a == nil {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/sirrobot01/decypharr/internal/config"
|
||||
"github.com/sirrobot01/decypharr/internal/logger"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
)
|
||||
|
||||
type QBit struct {
|
||||
@@ -12,7 +12,7 @@ type QBit struct {
|
||||
Password string
|
||||
DownloadFolder string
|
||||
Categories []string
|
||||
storage *store.TorrentStorage
|
||||
storage *wire.TorrentStorage
|
||||
logger zerolog.Logger
|
||||
Tags []string
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func New() *QBit {
|
||||
Password: cfg.Password,
|
||||
DownloadFolder: cfg.DownloadFolder,
|
||||
Categories: cfg.Categories,
|
||||
storage: store.Get().Torrents(),
|
||||
storage: wire.Get().Torrents(),
|
||||
logger: logger.New("qbit"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/sirrobot01/decypharr/internal/utils"
|
||||
"github.com/sirrobot01/decypharr/pkg/arr"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"strings"
|
||||
@@ -18,9 +18,9 @@ func (q *QBit) addMagnet(ctx context.Context, url string, arr *arr.Arr, debrid s
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing magnet link: %w", err)
|
||||
}
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
|
||||
importReq := store.NewImportRequest(debrid, q.DownloadFolder, magnet, arr, action, false, "", store.ImportTypeQBitTorrent)
|
||||
importReq := wire.NewImportRequest(debrid, q.DownloadFolder, magnet, arr, action, false, "", wire.ImportTypeQBitTorrent)
|
||||
|
||||
err = _store.AddTorrent(ctx, importReq)
|
||||
if err != nil {
|
||||
@@ -37,8 +37,8 @@ func (q *QBit) addTorrent(ctx context.Context, fileHeader *multipart.FileHeader,
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading file: %s \n %w", fileHeader.Filename, err)
|
||||
}
|
||||
_store := store.Get()
|
||||
importReq := store.NewImportRequest(debrid, q.DownloadFolder, magnet, arr, action, false, "", store.ImportTypeQBitTorrent)
|
||||
_store := wire.Get()
|
||||
importReq := wire.NewImportRequest(debrid, q.DownloadFolder, magnet, arr, action, false, "", wire.ImportTypeQBitTorrent)
|
||||
err = _store.AddTorrent(ctx, importReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process torrent: %w", err)
|
||||
@@ -46,19 +46,19 @@ func (q *QBit) addTorrent(ctx context.Context, fileHeader *multipart.FileHeader,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *QBit) ResumeTorrent(t *store.Torrent) bool {
|
||||
func (q *QBit) ResumeTorrent(t *wire.Torrent) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (q *QBit) PauseTorrent(t *store.Torrent) bool {
|
||||
func (q *QBit) PauseTorrent(t *wire.Torrent) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (q *QBit) RefreshTorrent(t *store.Torrent) bool {
|
||||
func (q *QBit) RefreshTorrent(t *wire.Torrent) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (q *QBit) GetTorrentProperties(t *store.Torrent) *TorrentProperties {
|
||||
func (q *QBit) GetTorrentProperties(t *wire.Torrent) *TorrentProperties {
|
||||
return &TorrentProperties{
|
||||
AdditionDate: t.AddedOn,
|
||||
Comment: "Debrid Blackhole <https://github.com/sirrobot01/decypharr>",
|
||||
@@ -83,7 +83,7 @@ func (q *QBit) GetTorrentProperties(t *store.Torrent) *TorrentProperties {
|
||||
}
|
||||
}
|
||||
|
||||
func (q *QBit) setTorrentTags(t *store.Torrent, tags []string) bool {
|
||||
func (q *QBit) setTorrentTags(t *wire.Torrent, tags []string) bool {
|
||||
torrentTags := strings.Split(t.Tags, ",")
|
||||
for _, tag := range tags {
|
||||
if tag == "" {
|
||||
@@ -101,7 +101,7 @@ func (q *QBit) setTorrentTags(t *store.Torrent, tags []string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (q *QBit) removeTorrentTags(t *store.Torrent, tags []string) bool {
|
||||
func (q *QBit) removeTorrentTags(t *wire.Torrent, tags []string) bool {
|
||||
torrentTags := strings.Split(t.Tags, ",")
|
||||
newTorrentTags := utils.RemoveItem(torrentTags, tags...)
|
||||
q.Tags = utils.RemoveItem(q.Tags, tags...)
|
||||
|
||||
@@ -4,19 +4,18 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/sirrobot01/decypharr/internal/config"
|
||||
)
|
||||
|
||||
// Mount creates a mount using the rclone RC API with retry logic
|
||||
func (m *Manager) Mount(provider, webdavURL string) error {
|
||||
return m.mountWithRetry(provider, webdavURL, 3)
|
||||
func (m *Manager) Mount(mountPath, provider, webdavURL string) error {
|
||||
return m.mountWithRetry(mountPath, provider, webdavURL, 3)
|
||||
}
|
||||
|
||||
// mountWithRetry attempts to mount with retry logic
|
||||
func (m *Manager) mountWithRetry(provider, webdavURL string, maxRetries int) error {
|
||||
func (m *Manager) mountWithRetry(mountPath, provider, webdavURL string, maxRetries int) error {
|
||||
if !m.IsReady() {
|
||||
if err := m.WaitForReady(30 * time.Second); err != nil {
|
||||
return fmt.Errorf("rclone RC server not ready: %w", err)
|
||||
@@ -34,7 +33,7 @@ func (m *Manager) mountWithRetry(provider, webdavURL string, maxRetries int) err
|
||||
time.Sleep(wait)
|
||||
}
|
||||
|
||||
if err := m.performMount(provider, webdavURL); err != nil {
|
||||
if err := m.performMount(mountPath, provider, webdavURL); err != nil {
|
||||
m.logger.Error().
|
||||
Err(err).
|
||||
Str("provider", provider).
|
||||
@@ -49,9 +48,8 @@ func (m *Manager) mountWithRetry(provider, webdavURL string, maxRetries int) err
|
||||
}
|
||||
|
||||
// performMount performs a single mount attempt
|
||||
func (m *Manager) performMount(provider, webdavURL string) error {
|
||||
func (m *Manager) performMount(mountPath, provider, webdavURL string) error {
|
||||
cfg := config.Get()
|
||||
mountPath := filepath.Join(cfg.Rclone.MountPath, provider)
|
||||
|
||||
// Create mount directory
|
||||
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
||||
@@ -94,6 +92,18 @@ func (m *Manager) performMount(provider, webdavURL string) error {
|
||||
"VolumeName": fmt.Sprintf("decypharr-%s", provider),
|
||||
}
|
||||
|
||||
if cfg.Rclone.AsyncRead != nil {
|
||||
mountOpt["AsyncRead"] = *cfg.Rclone.AsyncRead
|
||||
}
|
||||
|
||||
if cfg.Rclone.UseMmap {
|
||||
mountOpt["UseMmap"] = cfg.Rclone.UseMmap
|
||||
}
|
||||
|
||||
if cfg.Rclone.Transfers != 0 {
|
||||
mountOpt["Transfers"] = cfg.Rclone.Transfers
|
||||
}
|
||||
|
||||
configOpts := make(map[string]interface{})
|
||||
|
||||
if cfg.Rclone.BufferSize != "" {
|
||||
@@ -127,6 +137,19 @@ func (m *Manager) performMount(provider, webdavURL string) error {
|
||||
if cfg.Rclone.VfsReadAhead != "" {
|
||||
vfsOpt["ReadAhead"] = cfg.Rclone.VfsReadAhead
|
||||
}
|
||||
|
||||
if cfg.Rclone.VfsCacheMinFreeSpace != "" {
|
||||
vfsOpt["CacheMinFreeSpace"] = cfg.Rclone.VfsCacheMinFreeSpace
|
||||
}
|
||||
|
||||
if cfg.Rclone.VfsFastFingerprint {
|
||||
vfsOpt["FastFingerprint"] = cfg.Rclone.VfsFastFingerprint
|
||||
}
|
||||
|
||||
if cfg.Rclone.VfsReadChunkStreams != 0 {
|
||||
vfsOpt["ReadChunkStreams"] = cfg.Rclone.VfsReadChunkStreams
|
||||
}
|
||||
|
||||
if cfg.Rclone.NoChecksum {
|
||||
vfsOpt["NoChecksum"] = cfg.Rclone.NoChecksum
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func (m *Manager) RecoverMount(provider string) error {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Try to remount
|
||||
if err := m.Mount(provider, mountInfo.WebDAVURL); err != nil {
|
||||
if err := m.Mount(mountInfo.LocalPath, provider, mountInfo.WebDAVURL); err != nil {
|
||||
return fmt.Errorf("failed to recover mount for %s: %w", provider, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,13 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -261,50 +263,35 @@ func (m *Manager) Stop() error {
|
||||
case err := <-done:
|
||||
if err != nil && !errors.Is(err, context.Canceled) && !WasHardTerminated(err) {
|
||||
m.logger.Warn().Err(err).Msg("Rclone process exited with error")
|
||||
} else {
|
||||
m.logger.Info().Msg("Rclone process exited gracefully")
|
||||
}
|
||||
case <-time.After(10 * time.Second):
|
||||
case <-time.After(2 * time.Second):
|
||||
m.logger.Warn().Msg("Timeout waiting for rclone to exit, force killing")
|
||||
if err := m.cmd.Process.Kill(); err != nil {
|
||||
m.logger.Error().Err(err).Msg("Failed to force kill rclone process")
|
||||
return err
|
||||
// Check if the process already finished
|
||||
if !strings.Contains(err.Error(), "process already finished") {
|
||||
m.logger.Error().Err(err).Msg("Failed to force kill rclone process")
|
||||
return err
|
||||
}
|
||||
m.logger.Info().Msg("Process already finished during kill attempt")
|
||||
}
|
||||
// Wait a bit more for the kill to take effect
|
||||
|
||||
// Still wait for the Wait() to complete to clean up the process
|
||||
select {
|
||||
case <-done:
|
||||
m.logger.Info().Msg("Rclone process killed successfully")
|
||||
m.logger.Info().Msg("Rclone process cleanup completed")
|
||||
case <-time.After(5 * time.Second):
|
||||
m.logger.Error().Msg("Process may still be running after kill")
|
||||
m.logger.Error().Msg("Process cleanup timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up any remaining mount directories
|
||||
cfg := config.Get()
|
||||
if cfg.Rclone.MountPath != "" {
|
||||
m.cleanupMountDirectories(cfg.Rclone.MountPath)
|
||||
}
|
||||
|
||||
m.serverStarted = false
|
||||
m.logger.Info().Msg("Rclone RC server stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanupMountDirectories removes empty mount directories
|
||||
func (m *Manager) cleanupMountDirectories(_ string) {
|
||||
m.mountsMutex.RLock()
|
||||
defer m.mountsMutex.RUnlock()
|
||||
|
||||
for _, mount := range m.mounts {
|
||||
if mount.LocalPath != "" {
|
||||
// Try to remove the directory if it's empty
|
||||
if err := os.Remove(mount.LocalPath); err == nil {
|
||||
m.logger.Debug().Str("path", mount.LocalPath).Msg("Removed empty mount directory")
|
||||
}
|
||||
// Don't log errors here as the directory might not be empty, which is fine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// waitForServer waits for the RC server to become available
|
||||
func (m *Manager) waitForServer() {
|
||||
maxAttempts := 30
|
||||
@@ -352,7 +339,12 @@ func (m *Manager) makeRequest(req RCRequest, close bool) (*http.Response, error)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// Read the response body to get more details
|
||||
defer resp.Body.Close()
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
m.logger.Debug().Err(err).Msg("Failed to close response body")
|
||||
}
|
||||
}(resp.Body)
|
||||
var errorResp RCResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&errorResp); err != nil {
|
||||
return nil, fmt.Errorf("request failed with status %s, but could not decode error response: %w", resp.Status, err)
|
||||
|
||||
@@ -65,7 +65,7 @@ func (m *Mount) Mount(ctx context.Context) error {
|
||||
Str("mount_path", m.LocalPath).
|
||||
Msg("Creating mount via RC")
|
||||
|
||||
if err := m.rcManager.Mount(m.Provider, m.WebDAVURL); err != nil {
|
||||
if err := m.rcManager.Mount(m.LocalPath, m.Provider, m.WebDAVURL); err != nil {
|
||||
m.logger.Error().Str("provider", m.Provider).Msg("Mount operation failed")
|
||||
return fmt.Errorf("mount failed for %s", m.Provider)
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/sirrobot01/decypharr/internal/request"
|
||||
debridTypes "github.com/sirrobot01/decypharr/pkg/debrid/types"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
"net/http"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func (s *Server) handleIngests(w http.ResponseWriter, r *http.Request) {
|
||||
ingests := make([]debridTypes.IngestData, 0)
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
debrids := _store.Debrid()
|
||||
if debrids == nil {
|
||||
http.Error(w, "Debrid service is not enabled", http.StatusInternalServerError)
|
||||
@@ -42,7 +42,7 @@ func (s *Server) handleIngestsByDebrid(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
debrids := _store.Debrid()
|
||||
|
||||
if debrids == nil {
|
||||
@@ -92,7 +92,7 @@ func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
"go_version": runtime.Version(),
|
||||
}
|
||||
|
||||
debrids := store.Get().Debrid()
|
||||
debrids := wire.Get().Debrid()
|
||||
if debrids == nil {
|
||||
request.JSONResponse(w, stats, http.StatusOK)
|
||||
return
|
||||
@@ -121,7 +121,7 @@ func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
debridStat.Library = libraryStat
|
||||
|
||||
|
||||
// Get detailed account information
|
||||
accounts := client.Accounts().All()
|
||||
accountDetails := make([]map[string]any, 0)
|
||||
@@ -135,7 +135,7 @@ func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
maskedToken = "****"
|
||||
}
|
||||
|
||||
|
||||
accountDetail := map[string]any{
|
||||
"order": account.Order,
|
||||
"disabled": account.Disabled,
|
||||
@@ -153,7 +153,7 @@ func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
stats["debrids"] = debridStats
|
||||
|
||||
// Add rclone stats if available
|
||||
if rcManager := store.Get().RcloneManager(); rcManager != nil && rcManager.IsReady() {
|
||||
if rcManager := wire.Get().RcloneManager(); rcManager != nil && rcManager.IsReady() {
|
||||
rcStats, err := rcManager.GetStats()
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("Failed to get rclone stats")
|
||||
|
||||
@@ -3,7 +3,7 @@ package server
|
||||
import (
|
||||
"cmp"
|
||||
"encoding/json"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ func (s *Server) handleTautulli(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Invalid ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
repair := store.Get().Repair()
|
||||
repair := wire.Get().Repair()
|
||||
|
||||
mediaId := cmp.Or(payload.TmdbID, payload.TvdbID)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func (wb *Web) handleGetArrs(w http.ResponseWriter, r *http.Request) {
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
request.JSONResponse(w, _store.Arr().GetAll(), http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ func (wb *Web) handleAddContent(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
|
||||
results := make([]*store.ImportRequest, 0)
|
||||
results := make([]*wire.ImportRequest, 0)
|
||||
errs := make([]string, 0)
|
||||
|
||||
arrName := r.FormValue("arr")
|
||||
@@ -66,7 +66,7 @@ func (wb *Web) handleAddContent(w http.ResponseWriter, r *http.Request) {
|
||||
continue
|
||||
}
|
||||
|
||||
importReq := store.NewImportRequest(debridName, downloadFolder, magnet, _arr, action, downloadUncached, callbackUrl, store.ImportTypeAPI)
|
||||
importReq := wire.NewImportRequest(debridName, downloadFolder, magnet, _arr, action, downloadUncached, callbackUrl, wire.ImportTypeAPI)
|
||||
if err := _store.AddTorrent(ctx, importReq); err != nil {
|
||||
wb.logger.Error().Err(err).Str("url", url).Msg("Failed to add torrent")
|
||||
errs = append(errs, fmt.Sprintf("URL %s: %v", url, err))
|
||||
@@ -91,7 +91,7 @@ func (wb *Web) handleAddContent(w http.ResponseWriter, r *http.Request) {
|
||||
continue
|
||||
}
|
||||
|
||||
importReq := store.NewImportRequest(debridName, downloadFolder, magnet, _arr, action, downloadUncached, callbackUrl, store.ImportTypeAPI)
|
||||
importReq := wire.NewImportRequest(debridName, downloadFolder, magnet, _arr, action, downloadUncached, callbackUrl, wire.ImportTypeAPI)
|
||||
err = _store.AddTorrent(ctx, importReq)
|
||||
if err != nil {
|
||||
wb.logger.Error().Err(err).Str("file", fileHeader.Filename).Msg("Failed to add torrent")
|
||||
@@ -103,8 +103,8 @@ func (wb *Web) handleAddContent(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
request.JSONResponse(w, struct {
|
||||
Results []*store.ImportRequest `json:"results"`
|
||||
Errors []string `json:"errors,omitempty"`
|
||||
Results []*wire.ImportRequest `json:"results"`
|
||||
Errors []string `json:"errors,omitempty"`
|
||||
}{
|
||||
Results: results,
|
||||
Errors: errs,
|
||||
@@ -118,7 +118,7 @@ func (wb *Web) handleRepairMedia(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
|
||||
var arrs []string
|
||||
|
||||
@@ -186,7 +186,7 @@ func (wb *Web) handleGetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
// Merge config arrs, with arr Storage
|
||||
unique := map[string]config.Arr{}
|
||||
cfg := config.Get()
|
||||
arrStorage := store.Get().Arr()
|
||||
arrStorage := wire.Get().Arr()
|
||||
|
||||
// Add existing Arrs from storage
|
||||
for _, a := range arrStorage.GetAll() {
|
||||
@@ -276,7 +276,7 @@ func (wb *Web) handleUpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Update Arrs through the service
|
||||
storage := store.Get()
|
||||
storage := wire.Get()
|
||||
arrStorage := storage.Arr()
|
||||
|
||||
newConfigArrs := make([]config.Arr, 0)
|
||||
@@ -330,7 +330,7 @@ func (wb *Web) handleUpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (wb *Web) handleGetRepairJobs(w http.ResponseWriter, r *http.Request) {
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
request.JSONResponse(w, _store.Repair().GetJobs(), http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ func (wb *Web) handleProcessRepairJob(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "No job ID provided", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
if err := _store.Repair().ProcessJob(id); err != nil {
|
||||
wb.logger.Error().Err(err).Msg("Failed to process repair job")
|
||||
}
|
||||
@@ -361,7 +361,7 @@ func (wb *Web) handleDeleteRepairJob(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
_store.Repair().DeleteJobs(req.IDs)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
@@ -372,7 +372,7 @@ func (wb *Web) handleStopRepairJob(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "No job ID provided", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_store := store.Get()
|
||||
_store := wire.Get()
|
||||
if err := _store.Repair().StopJob(id); err != nil {
|
||||
wb.logger.Error().Err(err).Msg("Failed to stop repair job")
|
||||
http.Error(w, "Failed to stop job: "+err.Error(), http.StatusInternalServerError)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -149,10 +149,11 @@ class ConfigManager {
|
||||
if (!rcloneConfig) return;
|
||||
|
||||
const fields = [
|
||||
'enabled', 'mount_path', 'cache_dir', 'vfs_cache_mode', 'vfs_cache_max_size', 'vfs_cache_max_age',
|
||||
'enabled', 'rc_port', 'mount_path', 'cache_dir', 'transfers', 'vfs_cache_mode', 'vfs_cache_max_size', 'vfs_cache_max_age',
|
||||
'vfs_cache_poll_interval', 'vfs_read_chunk_size', 'vfs_read_chunk_size_limit', 'buffer_size',
|
||||
'uid', 'gid', 'vfs_read_ahead', 'attr_timeout', 'dir_cache_time', 'poll_interval', 'umask',
|
||||
'no_modtime', 'no_checksum', 'log_level'
|
||||
'no_modtime', 'no_checksum', 'log_level', 'vfs_cache_min_free_space', 'vfs_fast_fingerprint', 'vfs_read_chunk_streams',
|
||||
'async_read', 'use_mmap'
|
||||
];
|
||||
|
||||
fields.forEach(field => {
|
||||
@@ -273,7 +274,6 @@ class ConfigManager {
|
||||
<div class="form-control flex-1">
|
||||
<label class="label" for="debrid[${index}].download_api_keys">
|
||||
<span class="label-text font-medium">Download API Keys</span>
|
||||
<span class="badge badge-ghost badge-sm">Optional</span>
|
||||
</label>
|
||||
<div class="password-toggle-container">
|
||||
<textarea class="textarea textarea-bordered has-toggle font-mono h-full min-h-[200px]"
|
||||
@@ -290,7 +290,7 @@ class ConfigManager {
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label" for="debrid[${index}].folder">
|
||||
<span class="label-text font-medium">Mount/Rclone Folder</span>
|
||||
@@ -302,17 +302,6 @@ class ConfigManager {
|
||||
<span class="label-text-alt">Path where debrid files are mounted</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="debrid[${index}].rate_limit">
|
||||
<span class="label-text font-medium">Rate Limit</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered"
|
||||
name="debrid[${index}].rate_limit" id="debrid[${index}].rate_limit"
|
||||
placeholder="250/minute" value="250/minute">
|
||||
<div class="label">
|
||||
<span class="label-text-alt">API rate limit for this service</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="debrid[${index}].rclone_mount_path">
|
||||
<span class="label-text font-medium">Custom Rclone Mount Path</span>
|
||||
@@ -324,8 +313,21 @@ class ConfigManager {
|
||||
<div class="label">
|
||||
<span class="label-text-alt">Custom mount path for this debrid service. If empty, uses global rclone mount path.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="grid grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
<div class="form-control">
|
||||
<label class="label" for="debrid[${index}].rate_limit">
|
||||
<span class="label-text font-medium">Rate Limit</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered"
|
||||
name="debrid[${index}].rate_limit" id="debrid[${index}].rate_limit"
|
||||
placeholder="250/minute" value="250/minute">
|
||||
<div class="label">
|
||||
<span class="label-text-alt">API rate limit for this service</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="debrid[${index}].proxy">
|
||||
<span class="label-text font-medium">Proxy</span>
|
||||
@@ -337,6 +339,17 @@ class ConfigManager {
|
||||
<span class="label-text-alt">This proxy is used for this debrid account</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="debrid[${index}].minimum_free_slot">
|
||||
<span class="label-text font-medium">Minimum Free Slot</span>
|
||||
</label>
|
||||
<input type="number" class="input input-bordered"
|
||||
name="debrid[${index}].minimum_free_slot" id="debrid[${index}].minimum_free_slot"
|
||||
placeholder="1" value="1">
|
||||
<div class="label">
|
||||
<span class="label-text-alt">Minimum free slot for this debrid</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1102,6 +1115,7 @@ class ConfigManager {
|
||||
api_key: document.querySelector(`[name="debrid[${i}].api_key"]`).value,
|
||||
folder: document.querySelector(`[name="debrid[${i}].folder"]`).value,
|
||||
rate_limit: document.querySelector(`[name="debrid[${i}].rate_limit"]`).value,
|
||||
minimum_free_slot: parseInt(document.querySelector(`[name="debrid[${i}].minimum_free_slot"]`).value) || 0,
|
||||
rclone_mount_path: document.querySelector(`[name="debrid[${i}].rclone_mount_path"]`).value,
|
||||
proxy: document.querySelector(`[name="debrid[${i}].proxy"]`).value,
|
||||
download_uncached: document.querySelector(`[name="debrid[${i}].download_uncached"]`).checked,
|
||||
@@ -1231,15 +1245,22 @@ class ConfigManager {
|
||||
|
||||
return {
|
||||
enabled: getElementValue('enabled', false),
|
||||
rc_port: getElementValue('rc_port', "5572"),
|
||||
mount_path: getElementValue('mount_path'),
|
||||
buffer_size: getElementValue('buffer_size'),
|
||||
cache_dir: getElementValue('cache_dir'),
|
||||
transfers: getElementValue('transfers', 8),
|
||||
vfs_cache_mode: getElementValue('vfs_cache_mode', 'off'),
|
||||
vfs_cache_max_age: getElementValue('vfs_cache_max_age', '1h'),
|
||||
vfs_cache_max_size: getElementValue('vfs_cache_max_size'),
|
||||
vfs_cache_poll_interval: getElementValue('vfs_cache_poll_interval', '1m'),
|
||||
vfs_read_chunk_size: getElementValue('vfs_read_chunk_size', '128M'),
|
||||
vfs_read_chunk_size_limit: getElementValue('vfs_read_chunk_size_limit', 'off'),
|
||||
vfs_cache_min_free_space: getElementValue('vfs_cache_min_free_space', ''),
|
||||
vfs_fast_fingerprint: getElementValue('vfs_fast_fingerprint', false),
|
||||
vfs_read_chunk_streams: getElementValue('vfs_read_chunk_streams', 0),
|
||||
use_mmap: getElementValue('use_mmap', false),
|
||||
async_read: getElementValue('async_read', true),
|
||||
uid: getElementValue('uid', 0),
|
||||
gid: getElementValue('gid', 0),
|
||||
umask: getElementValue('umask', ''),
|
||||
|
||||
@@ -466,6 +466,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label" for="rclone.rc_port">
|
||||
<span class="label-text font-medium">RC Port</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered" name="rclone.rc_port" id="rclone.rc_port">
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label" for="rclone.log_level">
|
||||
<span class="label-text font-medium">Log Level</span>
|
||||
@@ -524,6 +531,15 @@
|
||||
<span class="label-text-alt">How long the kernel caches the attributes (size, modification time, etc.)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="rclone.transfers">
|
||||
<span class="label-text font-medium">Transfers</span>
|
||||
</label>
|
||||
<input type="number" class="input input-bordered" name="rclone.transfers" id="rclone.transfers" placeholder="8" min="1">
|
||||
<div class="label">
|
||||
<span class="label-text-alt">Number of file transfers to run in parallel</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -627,6 +643,36 @@
|
||||
<span class="label-text-alt">How often VFS cache dir gets cleaned</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label" for="rclone.vfs_cache_min_free_space">
|
||||
<span class="label-text font-medium">VFS Cache Min Free Space</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered" name="rclone.vfs_cache_min_free_space" id="rclone.vfs_cache_min_free_space" placeholder="1G">
|
||||
<div class="label">
|
||||
<span class="label-text-alt">Target minimum free space on the disk containing the cache</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label" for="rclone.vfs_disk_space_total">
|
||||
<span class="label-text font-medium">VFS Disk Space Total</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered" name="rclone.vfs_disk_space_total" id="rclone.vfs_disk_space_total" placeholder="1G">
|
||||
<div class="label">
|
||||
<span class="label-text-alt">Specify the total space of disk</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label" for="rclone.vfs_read_chunk_streams">
|
||||
<span class="label-text font-medium">VFS Read Chunk Streams</span>
|
||||
</label>
|
||||
<input type="number" class="input input-bordered" name="rclone.vfs_read_chunk_streams" id="rclone.vfs_read_chunk_streams" placeholder="4" min="0">
|
||||
<div class="label">
|
||||
<span class="label-text-alt">The number of parallel streams to read at once</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -636,7 +682,7 @@
|
||||
<h3 class="text-lg font-semibold mb-4 flex items-center">
|
||||
<i class="bi bi-gear mr-2"></i>Advanced Settings
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" name="rclone.no_modtime" id="rclone.no_modtime">
|
||||
@@ -656,6 +702,36 @@
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-start gap-3" for="rclone.async_read">
|
||||
<input type="checkbox" class="checkbox" name="rclone.async_read" id="rclone.async_read">
|
||||
<div>
|
||||
<span class="label-text font-medium">Async Read</span>
|
||||
<div class="label-text-alt">Use asynchronous reads</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-start gap-3" for="rclone.vfs_fast_fingerprint">
|
||||
<input type="checkbox" class="checkbox" name="rclone.vfs_fast_fingerprint" id="rclone.vfs_fast_fingerprint">
|
||||
<div>
|
||||
<span class="label-text font-medium">VFS Fast Fingerprint</span>
|
||||
<div class="label-text-alt">Use fast (less accurate) fingerprints for change detection</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-start gap-3" for="rclone.use_mmap">
|
||||
<input type="checkbox" class="checkbox" name="rclone.use_mmap" id="rclone.use_mmap">
|
||||
<div>
|
||||
<span class="label-text font-medium">Use Mmap</span>
|
||||
<div class="label-text-alt">Use fast (less accurate) fingerprints for change detection</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/sirrobot01/decypharr/internal/logger"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
"html/template"
|
||||
"os"
|
||||
)
|
||||
@@ -60,7 +60,7 @@ type Web struct {
|
||||
logger zerolog.Logger
|
||||
cookie *sessions.CookieStore
|
||||
templates *template.Template
|
||||
torrents *store.TorrentStorage
|
||||
torrents *wire.TorrentStorage
|
||||
}
|
||||
|
||||
func New() *Web {
|
||||
@@ -86,6 +86,6 @@ func New() *Web {
|
||||
logger: logger.New("ui"),
|
||||
templates: templates,
|
||||
cookie: cookieStore,
|
||||
torrents: store.Get().Torrents(),
|
||||
torrents: wire.Get().Torrents(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/sirrobot01/decypharr/internal/config"
|
||||
"github.com/sirrobot01/decypharr/pkg/store"
|
||||
"github.com/sirrobot01/decypharr/pkg/wire"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -97,7 +97,7 @@ func New() *WebDav {
|
||||
Handlers: make([]*Handler, 0),
|
||||
URLBase: urlBase,
|
||||
}
|
||||
for name, c := range store.Get().Debrid().Caches() {
|
||||
for name, c := range wire.Get().Debrid().Caches() {
|
||||
h := NewHandler(name, urlBase, c, c.Logger())
|
||||
w.Handlers = append(w.Handlers, h)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -310,7 +310,6 @@ func (s *Store) getTorrentPath(rclonePath string, debridTorrent *types.Torrent)
|
||||
for {
|
||||
torrentPath, err := debridTorrent.GetMountFolder(rclonePath)
|
||||
if err == nil {
|
||||
s.logger.Debug().Msgf("Found torrent path: %s", torrentPath)
|
||||
return torrentPath, err
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -29,7 +29,6 @@ func (s *Store) addToQueue(importReq *ImportRequest) error {
|
||||
|
||||
func (s *Store) StartQueueWorkers(ctx context.Context) error {
|
||||
// This function is responsible for starting the scheduled tasks
|
||||
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
@@ -45,7 +44,7 @@ func (s *Store) StartQueueWorkers(ctx context.Context) error {
|
||||
}), gocron.WithContext(ctx)); err != nil {
|
||||
s.logger.Error().Err(err).Msg("Failed to create slots tracking job")
|
||||
} else {
|
||||
s.logger.Trace().Msgf("Download link refresh job scheduled for every %s", "30s")
|
||||
s.logger.Trace().Msgf("Slots tracking job scheduled for every %s", "30s")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,13 +85,17 @@ func (s *Store) trackAvailableSlots(ctx context.Context) {
|
||||
availableSlots[name] = slots
|
||||
}
|
||||
|
||||
if len(availableSlots) == 0 {
|
||||
s.logger.Debug().Msg("No debrid clients available or no slots found")
|
||||
return // No debrid clients or slots available, nothing to process
|
||||
}
|
||||
|
||||
if s.importsQueue.Size() <= 0 {
|
||||
// Queue is empty, no need to process
|
||||
return
|
||||
}
|
||||
|
||||
for name, slots := range availableSlots {
|
||||
|
||||
s.logger.Debug().Msgf("Available slots for %s: %d", name, slots)
|
||||
// If slots are available, process the next import request from the queue
|
||||
for slots > 0 {
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
@@ -86,7 +86,10 @@ func Reset() {
|
||||
}
|
||||
|
||||
if instance.rcloneManager != nil {
|
||||
instance.rcloneManager.Stop()
|
||||
err := instance.rcloneManager.Stop()
|
||||
if err != nil {
|
||||
instance.logger.Error().Err(err).Msg("Failed to stop rclone manager")
|
||||
}
|
||||
}
|
||||
|
||||
if instance.importsQueue != nil {
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package store
|
||||
package wire
|
||||
|
||||
import "context"
|
||||
|
||||
Reference in New Issue
Block a user