- Add more rclone supports
- Add rclone log viewer - Add more stats to Stats page - Fix some minor bugs
This commit is contained in:
+21
-17
@@ -70,7 +70,10 @@ func (m *Manager) performMount(provider, webdavURL string) error {
|
||||
|
||||
// Clean up any stale mount first
|
||||
if exists && !existingMount.Mounted {
|
||||
m.forceUnmountPath(mountPath)
|
||||
err := m.forceUnmountPath(mountPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create rclone config for this provider
|
||||
@@ -82,14 +85,13 @@ func (m *Manager) performMount(provider, webdavURL string) error {
|
||||
mountArgs := map[string]interface{}{
|
||||
"fs": fmt.Sprintf("%s:", provider),
|
||||
"mountPoint": mountPath,
|
||||
"mountType": "mount", // Use standard FUSE mount
|
||||
"mountOpt": map[string]interface{}{
|
||||
"AllowNonEmpty": true,
|
||||
"AllowOther": true,
|
||||
"DebugFUSE": false,
|
||||
"DeviceName": fmt.Sprintf("decypharr-%s", provider),
|
||||
"VolumeName": fmt.Sprintf("decypharr-%s", provider),
|
||||
},
|
||||
}
|
||||
mountOpt := map[string]interface{}{
|
||||
"AllowNonEmpty": true,
|
||||
"AllowOther": true,
|
||||
"DebugFUSE": false,
|
||||
"DeviceName": fmt.Sprintf("decypharr-%s", provider),
|
||||
"VolumeName": fmt.Sprintf("decypharr-%s", provider),
|
||||
}
|
||||
|
||||
configOpts := make(map[string]interface{})
|
||||
@@ -102,12 +104,13 @@ func (m *Manager) performMount(provider, webdavURL string) error {
|
||||
// Only add _config if there are options to set
|
||||
mountArgs["_config"] = configOpts
|
||||
}
|
||||
vfsOpt := map[string]interface{}{
|
||||
"CacheMode": cfg.Rclone.VfsCacheMode,
|
||||
}
|
||||
vfsOpt["PollInterval"] = 0 // Poll interval not supported for webdav, set to 0
|
||||
|
||||
// Add VFS options if caching is enabled
|
||||
if cfg.Rclone.VfsCacheMode != "off" {
|
||||
vfsOpt := map[string]interface{}{
|
||||
"CacheMode": cfg.Rclone.VfsCacheMode,
|
||||
}
|
||||
|
||||
if cfg.Rclone.VfsCacheMaxAge != "" {
|
||||
vfsOpt["CacheMaxAge"] = cfg.Rclone.VfsCacheMaxAge
|
||||
@@ -130,22 +133,23 @@ func (m *Manager) performMount(provider, webdavURL string) error {
|
||||
if cfg.Rclone.NoModTime {
|
||||
vfsOpt["NoModTime"] = cfg.Rclone.NoModTime
|
||||
}
|
||||
|
||||
mountArgs["vfsOpt"] = vfsOpt
|
||||
}
|
||||
|
||||
// Add mount options based on configuration
|
||||
if cfg.Rclone.UID != 0 {
|
||||
mountArgs["mountOpt"].(map[string]interface{})["UID"] = cfg.Rclone.UID
|
||||
mountOpt["UID"] = cfg.Rclone.UID
|
||||
}
|
||||
if cfg.Rclone.GID != 0 {
|
||||
mountArgs["mountOpt"].(map[string]interface{})["GID"] = cfg.Rclone.GID
|
||||
mountOpt["GID"] = cfg.Rclone.GID
|
||||
}
|
||||
if cfg.Rclone.AttrTimeout != "" {
|
||||
if attrTimeout, err := time.ParseDuration(cfg.Rclone.AttrTimeout); err == nil {
|
||||
mountArgs["mountOpt"].(map[string]interface{})["AttrTimeout"] = attrTimeout.String()
|
||||
mountOpt["AttrTimeout"] = attrTimeout.String()
|
||||
}
|
||||
}
|
||||
|
||||
mountArgs["vfsOpt"] = vfsOpt
|
||||
mountArgs["mountOpt"] = mountOpt
|
||||
// Make the mount request
|
||||
req := RCRequest{
|
||||
Command: "mount/mount",
|
||||
|
||||
@@ -71,7 +71,7 @@ func (m *Manager) RecoverMount(provider string) error {
|
||||
}
|
||||
|
||||
// Wait a moment
|
||||
time.Sleep(2 * time.Second)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Try to remount
|
||||
if err := m.Mount(provider, mountInfo.WebDAVURL); err != nil {
|
||||
|
||||
+18
-8
@@ -24,7 +24,7 @@ type Manager struct {
|
||||
rcPort string
|
||||
rcUser string
|
||||
rcPass string
|
||||
configDir string
|
||||
rcloneDir string
|
||||
mounts map[string]*MountInfo
|
||||
mountsMutex sync.RWMutex
|
||||
logger zerolog.Logger
|
||||
@@ -61,10 +61,10 @@ func NewManager() *Manager {
|
||||
cfg := config.Get()
|
||||
|
||||
rcPort := "5572"
|
||||
configDir := filepath.Join(cfg.Path, "rclone")
|
||||
rcloneDir := filepath.Join(cfg.Path, "rclone")
|
||||
|
||||
// Ensure config directory exists
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
if err := os.MkdirAll(rcloneDir, 0755); err != nil {
|
||||
_logger := logger.New("rclone")
|
||||
_logger.Error().Err(err).Msg("Failed to create rclone config directory")
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func NewManager() *Manager {
|
||||
|
||||
return &Manager{
|
||||
rcPort: rcPort,
|
||||
configDir: configDir,
|
||||
rcloneDir: rcloneDir,
|
||||
mounts: make(map[string]*MountInfo),
|
||||
logger: logger.New("rclone"),
|
||||
ctx: ctx,
|
||||
@@ -98,12 +98,22 @@ func (m *Manager) Start(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
logFile := filepath.Join(logger.GetLogPath(), "rclone.log")
|
||||
|
||||
// Delete old log file if it exists
|
||||
if _, err := os.Stat(logFile); err == nil {
|
||||
if err := os.Remove(logFile); err != nil {
|
||||
return fmt.Errorf("failed to remove old rclone log file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"rcd",
|
||||
"--rc-addr", ":" + m.rcPort,
|
||||
"--rc-no-auth", // We'll handle auth at the application level
|
||||
"--config", filepath.Join(m.configDir, "rclone.conf"),
|
||||
"--log-level", "INFO",
|
||||
"--config", filepath.Join(m.rcloneDir, "rclone.conf"),
|
||||
"--log-level", cfg.Rclone.LogLevel,
|
||||
"--log-file", logFile,
|
||||
}
|
||||
if cfg.Rclone.CacheDir != "" {
|
||||
if err := os.MkdirAll(cfg.Rclone.CacheDir, 0755); err == nil {
|
||||
@@ -111,7 +121,6 @@ func (m *Manager) Start(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
m.cmd = exec.CommandContext(ctx, "rclone", args...)
|
||||
m.cmd.Dir = m.configDir
|
||||
|
||||
// Capture output for debugging
|
||||
var stdout, stderr bytes.Buffer
|
||||
@@ -119,9 +128,10 @@ func (m *Manager) Start(ctx context.Context) error {
|
||||
m.cmd.Stderr = &stderr
|
||||
|
||||
if err := m.cmd.Start(); err != nil {
|
||||
m.logger.Error().Str("stderr", stderr.String()).Str("stdout", stdout.String()).
|
||||
Err(err).Msg("Failed to start rclone RC server")
|
||||
return fmt.Errorf("failed to start rclone RC server: %w", err)
|
||||
}
|
||||
|
||||
m.serverStarted = true
|
||||
|
||||
// Wait for server to be ready in a goroutine
|
||||
|
||||
+12
-2
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/sirrobot01/decypharr/internal/config"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Mount represents a mount using the rclone RC client
|
||||
@@ -19,15 +20,24 @@ type Mount struct {
|
||||
}
|
||||
|
||||
// NewMount creates a new RC-based mount
|
||||
func NewMount(provider, webdavURL string, rcManager *Manager) *Mount {
|
||||
func NewMount(provider, customRcloneMount, webdavURL string, rcManager *Manager) *Mount {
|
||||
cfg := config.Get()
|
||||
mountPath := filepath.Join(cfg.Rclone.MountPath, provider)
|
||||
var mountPath string
|
||||
if customRcloneMount != "" {
|
||||
mountPath = customRcloneMount
|
||||
} else {
|
||||
mountPath = filepath.Join(cfg.Rclone.MountPath, provider)
|
||||
}
|
||||
|
||||
_url, err := url.JoinPath(webdavURL, provider)
|
||||
if err != nil {
|
||||
_url = fmt.Sprintf("%s/%s", webdavURL, provider)
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(_url, "/") {
|
||||
_url += "/"
|
||||
}
|
||||
|
||||
return &Mount{
|
||||
Provider: provider,
|
||||
LocalPath: mountPath,
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ func (m *Manager) GetStats() (*Stats, error) {
|
||||
}
|
||||
// Get bandwidth stats
|
||||
bwStats, err := m.GetBandwidthStats()
|
||||
if err == nil {
|
||||
if err == nil && bwStats != nil {
|
||||
stats.Bandwidth = *bwStats
|
||||
} else {
|
||||
fmt.Println("Failed to get rclone stats", err)
|
||||
|
||||
Reference in New Issue
Block a user