diff --git a/cmd/altmount/cmd/serve.go b/cmd/altmount/cmd/serve.go index 0ae72a38b..9c63c04ac 100644 --- a/cmd/altmount/cmd/serve.go +++ b/cmd/altmount/cmd/serve.go @@ -178,7 +178,7 @@ func runServe(cmd *cobra.Command, args []string) error { configManager.OnConfigChange(func(oldConfig, newConfig *config.Config) { structuralChange := oldConfig.SegmentCache.CachePath != newConfig.SegmentCache.CachePath || oldConfig.SegmentCache.MaxSizeGB != newConfig.SegmentCache.MaxSizeGB || - oldConfig.SegmentCache.ExpiryHours != newConfig.SegmentCache.ExpiryHours + intPtrValue(oldConfig.SegmentCache.ExpiryHours) != intPtrValue(newConfig.SegmentCache.ExpiryHours) if !structuralChange { return @@ -439,3 +439,13 @@ func waitForHTTPServer(ctx context.Context, port int) error { } } + +// intPtrValue returns the value pointed to by p, or 0 when p is nil. It is used +// to compare optional integer config fields (e.g. segment cache ExpiryHours) by +// value rather than by pointer identity. +func intPtrValue(p *int) int { + if p == nil { + return 0 + } + return *p +} diff --git a/cmd/altmount/cmd/setup.go b/cmd/altmount/cmd/setup.go index 50846f61a..54357cdf6 100644 --- a/cmd/altmount/cmd/setup.go +++ b/cmd/altmount/cmd/setup.go @@ -315,10 +315,18 @@ func initializeSegmentCache(ctx context.Context, cfg *config.Config, source *seg return nil } + // ExpiryHours is normalized in config.Validate (nil -> 24h); guard against + // nil defensively in case the cache is initialized outside the load path. A + // zero value is preserved and means "cache forever". + expiryHours := 24 + if cfg.SegmentCache.ExpiryHours != nil { + expiryHours = *cfg.SegmentCache.ExpiryHours + } + mgrCfg := segcache.ManagerConfig{ CachePath: cfg.SegmentCache.CachePath, MaxSizeBytes: int64(cfg.SegmentCache.MaxSizeGB) * 1024 * 1024 * 1024, - ExpiryDuration: time.Duration(cfg.SegmentCache.ExpiryHours) * time.Hour, + ExpiryDuration: time.Duration(expiryHours) * time.Hour, }.WithDefaults() mgr, err := segcache.NewManager(mgrCfg, slog.Default().With("component", "segcache")) diff --git a/frontend/src/components/config/StreamingConfigSection.tsx b/frontend/src/components/config/StreamingConfigSection.tsx index 502f713de..4d3612898 100644 --- a/frontend/src/components/config/StreamingConfigSection.tsx +++ b/frontend/src/components/config/StreamingConfigSection.tsx @@ -131,7 +131,8 @@ export function StreamingConfigSection({

Playback Failure Masking

- Debounce transient streaming errors (like propagation delays) by requiring multiple consecutive playback failures before triggering a redownload. + Debounce transient streaming errors (like propagation delays) by requiring multiple + consecutive playback failures before triggering a redownload.

Failure Threshold

- Number of consecutive failures required before declaring a file corrupted and requesting repair. + Number of consecutive failures required before declaring a file corrupted and + requesting repair.

- + {streamingData.failure_masking?.threshold ?? 3} - failures + + failures +
-
+
1 3 5 @@ -300,21 +304,30 @@ export function StreamingConfigSection({ Cache Expiry

- How long cached segments are kept before automatic eviction. + How long cached segments are kept before automatic eviction. Set to 0 to keep + them forever (bounded only by the maximum cache size).

- - {cacheData.expiry_hours} - - hours + {cacheData.expiry_hours === 0 ? ( + Forever + ) : ( + <> + + {cacheData.expiry_hours} + + + hours + + + )}
- 1h + Forever 42h 84h 126h diff --git a/frontend/src/components/system/ProviderStatusTable.tsx b/frontend/src/components/system/ProviderStatusTable.tsx index 66de8d6ed..99a1fdfd7 100644 --- a/frontend/src/components/system/ProviderStatusTable.tsx +++ b/frontend/src/components/system/ProviderStatusTable.tsx @@ -492,8 +492,9 @@ export function ProviderStatusTable({ )} {hasQuota && (
-
- Quota: {formatBytes(provider.quota_used || 0, 2, false, true)} / {formatBytes(provider.quota_bytes || 0, 2, false, true)} +
+ Quota: {formatBytes(provider.quota_used || 0, 2, false, true)} /{" "} + {formatBytes(provider.quota_bytes || 0, 2, false, true)}
{provider.quota_reset_at && (
diff --git a/frontend/src/pages/HealthPage.tsx b/frontend/src/pages/HealthPage.tsx index 8dcdde464..338ed69c7 100644 --- a/frontend/src/pages/HealthPage.tsx +++ b/frontend/src/pages/HealthPage.tsx @@ -1,8 +1,8 @@ -import { apiClient } from "../api/client"; import { useQueryClient } from "@tanstack/react-query"; import { FileCheck, RefreshCw, RotateCcw, Settings, ShieldCheck, Trash2 } from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; +import { apiClient } from "../api/client"; import { ErrorAlert } from "../components/ui/ErrorAlert"; import { Pagination } from "../components/ui/Pagination"; import { useConfirm } from "../contexts/ModalContext"; diff --git a/frontend/src/pages/HealthPage/components/HealthTable/HealthTable.tsx b/frontend/src/pages/HealthPage/components/HealthTable/HealthTable.tsx index 2372126bf..99e2dcfac 100644 --- a/frontend/src/pages/HealthPage/components/HealthTable/HealthTable.tsx +++ b/frontend/src/pages/HealthPage/components/HealthTable/HealthTable.tsx @@ -108,7 +108,12 @@ export function HealthTable({ onSelectAll={onSelectAll} onSelectAllPages={onSelectAllPages} onSort={onSort} - allowSelectAllPages={["pending", "checking", "corrupted", "repair_triggered"].includes(statusFilter)} + allowSelectAllPages={[ + "pending", + "checking", + "corrupted", + "repair_triggered", + ].includes(statusFilter)} /> {data.map((item: FileHealth) => ( diff --git a/frontend/src/pages/HealthPage/components/HealthTable/HealthTableHeader.tsx b/frontend/src/pages/HealthPage/components/HealthTable/HealthTableHeader.tsx index 319dd2b2e..d18030572 100644 --- a/frontend/src/pages/HealthPage/components/HealthTable/HealthTableHeader.tsx +++ b/frontend/src/pages/HealthPage/components/HealthTable/HealthTableHeader.tsx @@ -27,7 +27,7 @@ export function HealthTableHeader({
-