Add more rclone flags, fix minor issues
This commit is contained in:
+30
-7
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+21
-29
@@ -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)
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user