Add more rclone flags, fix minor issues

This commit is contained in:
Mukhtar Akere
2025-08-23 06:00:07 +01:00
parent b0a698f15e
commit f8667938b6
27 changed files with 256 additions and 123 deletions

View File

@@ -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

View File

@@ -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', ''),

View File

@@ -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>

View File

@@ -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(),
}
}