Key Services and Utilities
July 3, 2026 · View on GitHub
This page is a practical reference for the most-used classes in Lidarr.Plugin.Common. It shows what each one is for, when to use it, and a minimal example. Pair this with Flow.md (builder → options → executor → cache) for the big picture.
HTTP Request Flow
StreamingApiRequestBuilder— compose requests and stamp standardizedHttpRequestMessage.Optionsused by caching/dedup.HttpClientExtensions.ExecuteWithResilienceAsync— unified retries, deadlines, and per-host concurrency caps.HttpClientResilienceExtensions.ExecuteWithResilienceAndCachingAsync— add GET caching + 304 revalidation.RequestDeduplicator— single-flight identical GETs to avoid cache stampedes.
Example: GET with defaults, resiliency, dedup, and cache
var builder = new StreamingApiRequestBuilder("https://api.example.com")
.WithStreamingDefaults(userAgent: "MyPlugin/1.0")
.WithPolicy(ResiliencePolicy.Search)
.WithAuthScope("user:1234")
.Endpoint("v1/search")
.Query("q", term)
.Get();
using var request = builder.Build();
using var response = await httpClient.ExecuteWithResilienceAndCachingAsync(
request,
resilience: myResilienceProvider,
cache: myCache,
deduplicator: myDeduplicator,
conditionalState: myValidators,
cancellationToken: token);
response.EnsureSuccessStatusCode();
Notes
- Builder stamps standardized options (
EndpointKey,ParametersKey,ProfileKey,AuthScopeKey) used for cache keys and dedup keys. - 301/302 auto-follow only for safe methods; 307/308 preserve method/body. Non-idempotent calls are surfaced to the caller for control.
Resilience
ResiliencePolicy— immutable profile (retries, jittered backoff, retry budget, per-host concurrency caps, per-request timeout). Built-ins:Default,Search,Lookup,Streaming,Authentication,Metadata.
Customize
// Tighter search profile
var quickSearch = ResiliencePolicy.Search.With(
maxRetries: 3,
retryBudget: TimeSpan.FromSeconds(12),
perRequestTimeout: TimeSpan.FromSeconds(8));
Gotchas
- Pass a
perRequestTimeoutonly when you need a hard per-call deadline. Cancellation tokens are always respected. - The executor applies per-host gates and an optional aggregate host cap to avoid starving the host when multiple profiles target the same origin.
Caching
StreamingResponseCache— in-memory DTO cache (never storesHttpResponseMessage). Supports endpoint policies, sliding expiration with a refresh window, absolute caps, and a short stale grace to enable race-free 304 flows.
Key behaviors
- Cache keys:
service|/endpoint|canonical-params[|scope:abcdef]. Canonical parameters come from the builder; sensitive keys are filtered by default. - 304 path returns a synthetic 200 from cached bytes and refreshes TTL. A
XArrCache: revalidatedheader is added for observability.
Conditional validators
// Implement IConditionalRequestState to persist ETag/Last-Modified per cache key
public sealed class FileConditionalState : IConditionalRequestState { /* ... */ }
Deduplication
RequestDeduplicator— coalesces identical GETs (single-flight) and shares a buffered record among awaiters.
var key = HttpClientExtensions.BuildRequestDedupKey(request);
var response = await deduplicator.GetOrCreateAsync(key, () => http.SendAsync(request, token), token);
Authentication
OAuthDelegatingHandler— injects bearer tokens; single-flight refresh on 401 using anIStreamingTokenProvider.TokenDelegatingHandler— simple bearer injection when refresh is managed elsewhere.StreamingTokenManager<TSession, TCredentials>— refresh/persist session models, timer-assisted expiry checks, and events.
Example: token manager + OAuth handler
// Build a token provider backed by your settings + token manager
var tokenProvider = new MyTokenProvider(settings, tokenManager);
var http = new HttpClient(new OAuthDelegatingHandler(tokenProvider, logger));
See also: how-to/AUTHENTICATE_OAUTH.md and how-to/TOKEN_MANAGER.md.
Downloads
SimpleDownloadOrchestrator— resumable partial downloads (Range + If-Range with ETag/Last-Modified), atomic file moves, progress, and simple resume checkpoints.
var orchestrator = new SimpleDownloadOrchestrator(
"MyService", http,
getAlbumAsync: id => svc.GetAlbumAsync(id),
getTrackAsync: id => svc.GetTrackAsync(id),
getAlbumTrackIdsAsync: id => svc.GetAlbumTrackIdsAsync(id),
getStreamAsync: (id, q) => svc.GetStreamAsync(id, q));
Rate Limiting & Performance
UniversalAdaptiveRateLimiter— per-service, per-endpoint adaptive RPM with success-based increases and error/429 backoff. Use with indexers or custom clients.PerformanceMonitor/MemoryHealthMonitor— coarse-grained timing/health helpers for long-running operations.
Diagnostics
DiagnosticTapHandler— trace request/response (with masking) for deep debugging. Feature-flag viaLIDARR_PLUGIN_HTTP_TAP=1.RateLimitTelemetryHandler— emits counters and basic headers; useful during service tuning.- Observability: see Telemetry.md for Activities and Counters emitted by the HTTP pipeline and cache.
Diagnostic type and error-code constants
Use the canonical constants in DiagnosticTypes and DiagnosticErrorCodes (namespace Lidarr.Plugin.Common.Abstractions.Diagnostics) instead of magic strings so all plugins report consistent values.
| Class | Constants | Source |
|---|---|---|
DiagnosticTypes | AuthValidate, Connectivity, StreamProbe, CatalogAccess | src/Abstractions/Diagnostics/DiagnosticTypes.cs |
DiagnosticErrorCodes | AuthFailed, ConnectionFailed, RateLimited, Timeout, RegionBlocked, ValidationFailed, NotFound, ServerError | src/Abstractions/Diagnostics/DiagnosticErrorCodes.cs |
PluginErrorCode enum
PluginErrorCode (namespace Lidarr.Plugin.Abstractions.Results) provides 14 standardised error codes for PluginOperationResult. Use these instead of ad-hoc codes so the host and diagnostics layer can classify failures uniformly.
Values: None, Unknown, ValidationFailed, NotFound, Unauthorized, AuthenticationExpired, RateLimited, Timeout, Cancelled, ProviderUnavailable, Unsupported, QuotaExceeded, Conflict, ParsingFailed, NetworkFailure.
Source: src/Abstractions/Results/PluginErrorCode.cs.
Configuration
PluginConfigRoots— resolves the per-plugin configuration root directory. Override with theLIDARR_PLUGIN_CONFIGenvironment variable (highest priority, useful for tests and CI). Falls back through Docker/config,%AppData%, XDG, and$HOME/.config. Source:src/Hosting/PluginConfigRoots.cs.
Safety & Utilities
SafeOperationExecutor— defensive wrappers: timeouts, try-execute patterns, and IO retry for transient sharing violations.FileNameSanitizer/Sanitize— filesystem-safe names and URL-safe components.RequestSigning/IRequestSigner— MD5/HMAC helpers for signature schemes.
Streaming Plugin Base Classes
StreamingPlugin<TModule, TSettings>— main plugin entry point. Wires DI, settings lifecycle, manifest loading, and the host bridge. Inherit from this to create a streaming-service plugin. → Plugin BridgeStreamingPluginModule— base class for streaming-service plugin modules (DI registration, service wiring).BaseStreamingIndexer<T>— base class for streaming-service indexers. Provides search, rate-limit integration, and release cleanup.BaseStreamingDownloadClient<T>— base class for streaming-service download clients. Provides tracked download lifecycle and path safety.
HTTP Execution
CachingHttpExecutor— HTTP executor that integrates caching with the resilience pipeline. Use when you need automatic GET caching alongside retries and dedup.
Resilience & Circuit Breaking
AdvancedCircuitBreaker— circuit breaker with dual-trip logic: trips on consecutive failures or failure-rate threshold within a sampling window. Configurable half-open probe interval.NetworkResilienceService— coordinates resilience for batch operations and non-HTTP workflows (bulk lookups, metadata enrichment). WrapsAdvancedCircuitBreakerwith per-endpoint tracking.
Authentication Failure Handling
SlidingWindowAuthFailureHandler—IAuthFailureHandlerwith K-of-N-in-W sliding-window failure threshold semantics. Trips when K failures occur within window W; auto-resets after cooldown. → CHANGELOG v1.7.0
Search Intelligence
SearchQuerySanitizer— sanitizes raw artist/album strings and builds ordered fallback tiers (BuildPlan). Each tier is a best-first variant list (accent-folded, connective-expanded, NFKC, confusable-folded). UseBuildPlanto get the full tier structure; the executor iterates tiers.SearchPlanExecutor— drives a pre-builtSearchPlan(fromBuildPlan) against any async search function, applying aSearchStopPolicy(AccumulateAll,StopAfterFirstTierWithResults,StopAfterFirstVariantWithResults). ThrowsAllSearchVariantsFailed(viaThrowAllFailed) when every variant returned empty.CappedSearchChain— for capping plugins (e.g. qobuz) that cannot or should not issue all plan variants: caps combined/album-only queries to a maximum ofmaxOverSpecific, then unconditionally appends the artist-only fallback so it is never truncated away. This is the fix for the "Bleu Jeans Bleu / Record n°V returned 0 results" bug — aTake(N)that dropped the fallback tier. Plugins backed byCappedSearchChaininherit the fallback-survival guarantee with cross-plugin test coverage.
// Build the plan and cap the over-specific queries to 3, always preserving the artist-only fallback.
var plan = SearchQuerySanitizer.BuildPlan(artist, album);
var overSpecific = plan.Tiers.Count > 0 ? plan.Combined : Array.Empty<string>();
var artistOnly = plan.Tiers.Count > 1 ? plan.Tiers[1].FirstOrDefault() : null;
var queries = CappedSearchChain.Build(overSpecific, artistOnly, maxOverSpecific: 3);
See also: SearchRequestChainComplianceTestBase (TestKit) — compliance axis that drives the real request generator and verifies the chain preserves the full plan (including the fallback tier). Opt in to RequiresExactPlanSequence for full-chain plugins to also enforce duplicate-free exact ordering.
HostBridge Subsystem
HostBridgeDownloadOrchestrator— centralizes fire-and-forget download enqueue pattern for streaming plugins.HostBridgeRuntimeCache<TRuntime, TSettings>— generic singleton-runtime cache for per-plugin state keyed by settings instance.AlbumSizeEstimator— estimates album byte size from duration × bitrate with fallback ladder.MultiQualityReleaseBuilder— builds oneReleaseInfoper quality tier.AlbumReleaseInfoBuilder— buildsGuid,DownloadUrl, andTitlefields with edition/explicit/live markers.
→ Full HostBridge documentation: HOSTBRIDGE.md
Lyrics
LyricsEnricher— canonical synced-lyrics enricher shared across plugins. Orchestrates LRCLIB fallback with native-source lookup.LrclibClient— minimal client for the LRCLIB public lyrics API.
Storage
JsonFileStore— JSON file persistence for plugin state (tokens, session data, user preferences). Handles atomic write, read, and schema migration.