<?xml version="1.0"?>
<doc>
    <assembly>
        <name>PDM.Core</name>
    </assembly>
    <members>
        <member name="T:PDM.Core.Abstractions.IDownloadRepository">
            <summary>
            The long-term catalog of every download the user has ever added. Distinct from the
            per-download sidecar state used by the transfer engine (see <see cref="T:PDM.Core.Abstractions.IDownloadStateStore"/>).
            The repository powers the UI library view, search/filter, and history-based features.
            </summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadRepository.InitializeAsync(System.Threading.CancellationToken)">
            <summary>Creates the underlying storage if it does not exist.</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadRepository.UpsertAsync(PDM.Core.Models.DownloadState,System.Threading.CancellationToken)">
            <summary>Inserts or updates a record for the given state.</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadRepository.GetAsync(System.Guid,System.Threading.CancellationToken)">
            <summary>Loads a single record by id, or null when not found.</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadRepository.DeleteAsync(System.Guid,System.Threading.CancellationToken)">
            <summary>Deletes a single record.</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadRepository.ListAsync(System.Threading.CancellationToken)">
            <summary>Returns every record ordered by creation time (newest first).</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadRepository.ListByStatusAsync(System.Collections.Generic.IEnumerable{PDM.Core.Models.DownloadStatus},System.Threading.CancellationToken)">
            <summary>Returns records whose status matches any of <paramref name="statuses"/>.</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadRepository.ListByCategoryAsync(PDM.Core.Models.DownloadCategory,System.Threading.CancellationToken)">
            <summary>Returns records for the given category.</summary>
        </member>
        <member name="T:PDM.Core.Abstractions.IDownloadStateStore">
            <summary>
            Persists and restores <see cref="T:PDM.Core.Models.DownloadState"/> so downloads survive pauses,
            application restarts, and crashes. Implementations must save atomically to avoid
            corrupting state if the process is terminated mid-write.
            </summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadStateStore.SaveAsync(PDM.Core.Models.DownloadState,System.Threading.CancellationToken)">
            <summary>Persists the given state, overwriting any previous copy.</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadStateStore.LoadAsync(System.Guid,System.Threading.CancellationToken)">
            <summary>Loads previously saved state by id, or <c>null</c> if none exists.</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IDownloadStateStore.DeleteAsync(System.Guid,System.Threading.CancellationToken)">
            <summary>Removes persisted state for the given id, if present.</summary>
        </member>
        <member name="T:PDM.Core.Abstractions.INotificationService">
            <summary>
            Displays user-visible notifications. UI-agnostic so the manager and other headless
            components can raise events without depending on a specific presentation stack.
            </summary>
        </member>
        <member name="M:PDM.Core.Abstractions.INotificationService.ShowInfo(System.String,System.String)">
            <summary>Shows an informational notification. Silently no-ops when notifications are disabled.</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.INotificationService.ShowSuccess(System.String,System.String)">
            <summary>Shows a success notification (e.g. download completed).</summary>
        </member>
        <member name="M:PDM.Core.Abstractions.INotificationService.ShowError(System.String,System.String)">
            <summary>Shows an error notification (e.g. download failed).</summary>
        </member>
        <member name="T:PDM.Core.Abstractions.NullNotificationService">
            <summary>An <see cref="T:PDM.Core.Abstractions.INotificationService"/> that discards every call. Useful for tests and headless runs.</summary>
        </member>
        <member name="F:PDM.Core.Abstractions.NullNotificationService.Instance">
            <summary>Shared instance.</summary>
        </member>
        <member name="T:PDM.Core.Abstractions.IRemoteFileInspector">
            <summary>
            Probes a remote URL to discover the metadata needed to plan a download:
            total size, range support, effective (post-redirect) URL, and a file name.
            </summary>
        </member>
        <member name="M:PDM.Core.Abstractions.IRemoteFileInspector.InspectAsync(System.Uri,System.String,System.Threading.CancellationToken)">
            <summary>
            Inspects <paramref name="url"/> and returns discovered metadata.
            Implementations must follow redirects and should degrade gracefully when a
            server omits headers (e.g. reporting an unknown size rather than throwing).
            </summary>
            <param name="url">The resource to inspect.</param>
            <param name="referrer">
            Optional referrer sent as the <c>Referer</c> header on the probe. Many servers reject a
            bare request (HTTP 403) for hot-link-protected files unless it carries the originating
            page, so forwarding the browser-captured referrer lets the probe succeed as it did in the
            browser. Ignored when null, empty, or not an absolute URL.
            </param>
            <param name="cancellationToken">Token used to cancel probing.</param>
        </member>
        <member name="T:PDM.Core.Downloading.WriteChunk">
            <summary>
            A single unit of work handed from a network reader to the disk writer: a rented buffer to be
            written at an absolute file offset. The buffer belongs to the shared array pool and is returned by
            the writer once written, so producers must not touch it after enqueuing.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.DiskWriteQueue">
             <summary>
             Decouples network reads from disk writes. Network connections (producers) read from their sockets
             into pooled buffers and hand them to this queue; a pool of writer tasks drains the queue to the
             output file at the correct offsets. This is the key to stable, high download speed:
            
             <para><b>Why decoupling.</b> Disks accept writes in bursts (the OS write cache fills, then flushes).
             If a connection wrote inline it would stall on every flush and its socket would go idle, producing a
             "speed drops to 0 then spikes" sawtooth and false "waiting for server" states. Buffering writes in
             memory (bounded) lets connections keep draining their sockets continuously.</para>
            
             <para><b>Why several writers.</b> A single writer awaiting one positional write at a time
             keeps only ONE I/O in flight, so its ceiling is chunk-size ÷ write-latency — around
             100-250 MB/s on typical storage regardless of how fast the network is. On a fast link that made the
             writer, not the network, the bottleneck: throughput peaked then collapsed to KB/s every time the
             bounded queue filled and blocked every reader. Issuing several writes concurrently raises the I/O
             queue depth so fast NVMe/premium storage can be saturated, which both raises peak speed and stops
             the queue from filling in the first place.</para>
            
             <para><b>Durable-offset correctness with parallel writes.</b> Concurrent writes can complete out of
             order, so a segment's durable (resume) offset must not simply jump to the newest completion — that
             would claim bytes as safe while an earlier chunk was still in flight, and a crash at that moment
             would leave a hole. Completions are therefore accounted per segment and the durable offset advances
             only across a <em>contiguous</em> prefix; out-of-order completions are held until the gap before them
             is filled. The out-of-order window is at most the writer count, so this stays tiny.</para>
             </summary>
        </member>
        <member name="P:PDM.Core.Downloading.DiskWriteQueue.Failure">
            <summary>The first write failure observed, if any. Non-null means the download must fail.</summary>
        </member>
        <member name="M:PDM.Core.Downloading.DiskWriteQueue.BeginSegment(System.Int32,System.Int64,System.Int64)">
            <summary>
            Registers the byte offset a segment's writes start from, so contiguous accounting knows where the
            durable prefix begins. Must be called before any chunk for that segment is enqueued.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DiskWriteQueue.EnqueueAsync(PDM.Core.Downloading.WriteChunk,System.Threading.CancellationToken)">
            <summary>
            Hands a chunk to the writers, awaiting queue space when the disk is behind (backpressure). If a
            write has already failed, the buffer is returned and the failure is surfaced immediately so the
            producer stops reading.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DiskWriteQueue.NoteCompleted(System.Int32,System.Int64,System.Int32)">
            <summary>
            Records a completed write and advances the segment's durable prefix if this completion (plus any
            previously held out-of-order completions) extends it contiguously.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DiskWriteQueue.CompleteAndDrainAsync">
            <summary>
            Signals no more chunks will be enqueued and waits for every queued write to reach disk. Throws
            if any write failed. Call at a round boundary once all producers have stopped, so the durable
            offset reflects everything that was read.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.DiskWriteQueue.SegmentWriteAccount">
            <summary>
            Tracks one segment's contiguous written prefix, holding completions that arrive out of order
            until the bytes before them land. Keeps the persisted resume offset truthful when several writes
            are in flight at once.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DiskWriteQueue.SegmentWriteAccount.Add(System.Int64,System.Int32,System.Int64@)">
            <summary>
            Adds a completed write. Returns true (with the new durable length) when the contiguous prefix
            advanced, false when the completion was held pending an earlier gap.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.DownloadEngine">
            <summary>
            High-level entry point for creating and executing downloads. It probes a URL,
            resolves a collision-free output path, plans segmentation, and runs the transfer
            via a <see cref="T:PDM.Core.Downloading.DownloadWorker"/>. Pause, resume, and cancel are expressed through
            the caller's <see cref="T:System.Threading.CancellationToken"/>: cancel the token to stop, then either
            call <see cref="M:PDM.Core.Downloading.DownloadEngine.RunAsync(PDM.Core.Models.DownloadState,System.IProgress{PDM.Core.Models.DownloadProgress},PDM.Core.Models.DownloadOptions,PDM.Core.Downloading.SpeedLimiter,System.Threading.CancellationToken)"/> again to resume from persisted state, or delete the
            state to abandon it. Higher-level queueing lives in the download manager layer.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadEngine.PrepareAsync(System.Uri,System.String,System.String,System.Nullable{PDM.Core.Models.DownloadCategory},PDM.Core.Models.OverwritePolicy,System.Boolean,PDM.Core.Models.DownloadOptions,System.String,System.Threading.CancellationToken)">
            <summary>
            Probes <paramref name="url"/>, plans the download, and persists an initial
            <see cref="T:PDM.Core.Models.DownloadState"/> ready to be run. Does not transfer any file data.
            </summary>
            <param name="url">The resource to download.</param>
            <param name="destinationDirectory">Directory the final file will be written to.</param>
            <param name="fileNameOverride">Optional explicit file name; server-suggested name is used otherwise.</param>
            <param name="category">Category classification (auto-detected from the file name when null).</param>
            <param name="overwritePolicy">How to handle a destination path that already exists.</param>
            <param name="allowWebPage">
            When false (the default), the engine throws <see cref="T:PDM.Core.Downloading.LikelyWebPageException"/> if the URL
            resolves to an HTML page. Set to true to intentionally download the page's HTML source.
            </param>
            <param name="options">Optional per-download options; engine defaults are used otherwise.</param>
            <param name="referrer">
            Optional originating page sent as the <c>Referer</c> header on the inspection probe and
            persisted on the state so every segment request carries it too. Lets hot-link-protected
            downloads that the browser could fetch succeed here as well. Ignored when null/empty.
            </param>
            <param name="cancellationToken">Token used to cancel probing.</param>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadEngine.PrepareFromInfoAsync(System.Uri,PDM.Core.Models.RemoteFileInfo,System.String,System.String,System.Nullable{PDM.Core.Models.DownloadCategory},PDM.Core.Models.OverwritePolicy,System.Boolean,PDM.Core.Models.DownloadOptions,System.String,System.Threading.CancellationToken)">
            <summary>
            Same as <see cref="M:PDM.Core.Downloading.DownloadEngine.PrepareAsync(System.Uri,System.String,System.String,System.Nullable{PDM.Core.Models.DownloadCategory},PDM.Core.Models.OverwritePolicy,System.Boolean,PDM.Core.Models.DownloadOptions,System.String,System.Threading.CancellationToken)"/> but uses an already-obtained <paramref name="info"/> instead
            of probing the URL again. This lets a caller that probed for duplicate detection reuse that
            single probe for the actual prepare, so a new download never costs two network round trips.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadEngine.InspectAsync(System.Uri,System.String,System.Threading.CancellationToken)">
            <summary>
            Probes <paramref name="url"/> and returns its <see cref="T:PDM.Core.Models.RemoteFileInfo"/> without
            creating or persisting any download state. Used by the "change/refresh URL" flow to
            validate that a replacement link points at the same file before it is applied.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadEngine.RunAsync(PDM.Core.Models.DownloadState,System.IProgress{PDM.Core.Models.DownloadProgress},PDM.Core.Models.DownloadOptions,PDM.Core.Downloading.SpeedLimiter,System.Threading.CancellationToken)">
            <summary>
            Runs (or resumes) the download described by <paramref name="state"/>. Progress is
            reported through <paramref name="progress"/>. Cancelling <paramref name="cancellationToken"/>
            pauses the transfer and preserves resumable state.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadEngine.DownloadAsync(System.Uri,System.String,System.String,System.IProgress{PDM.Core.Models.DownloadProgress},System.Nullable{PDM.Core.Models.DownloadCategory},PDM.Core.Models.OverwritePolicy,System.Boolean,PDM.Core.Models.DownloadOptions,System.Threading.CancellationToken)">
            <summary>
            Convenience method that prepares and immediately runs a download of <paramref name="url"/>.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadEngine.EnsureSufficientDiskSpace(System.String,System.Nullable{System.Int64})">
            <summary>
            Throws a clear <see cref="T:PDM.Core.Downloading.DownloadException"/> when the destination volume does not have room for
            a file of <paramref name="totalBytes"/>. A small headroom margin is required on top of the file
            size so the download does not fill the disk to the last byte. No-op when the size is unknown or
            the volume's free space cannot be determined.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.DownloadException">
            <summary>Raised when a download fails in a way that cannot be automatically recovered.</summary>
        </member>
        <member name="T:PDM.Core.Downloading.DownloadWorker">
            <summary>
            Executes a single download described by a <see cref="T:PDM.Core.Models.DownloadState"/>. Downloads all
            segments concurrently, writes them into a preallocated ".pdmdownload" part file at
            their correct offsets, throttles to an optional speed cap, retries transient failures
            with exponential backoff, reports progress, and persists durable state for resume.
            The worker is resumable: it always starts each segment from its persisted
            <see cref="P:PDM.Core.Models.DownloadSegment.BytesDownloaded"/> offset.
            </summary>
        </member>
        <member name="F:PDM.Core.Downloading.DownloadWorker.PartSuffix">
            <summary>Suffix appended to the destination path for the in-progress part file.</summary>
        </member>
        <member name="F:PDM.Core.Downloading.DownloadWorker.ThroughputGainThreshold">
            <summary>
            Minimum throughput multiplier an added connection must deliver to be considered beneficial
            during adaptive probing (1.15 = at least 15% more goodput). Below this, probing stops.
            </summary>
        </member>
        <member name="P:PDM.Core.Downloading.DownloadWorker.PartPath">
            <summary>Absolute path to the part file being written.</summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.RunAsync(System.Threading.CancellationToken)">
            <summary>
            Runs the download to completion. Throws <see cref="T:System.OperationCanceledException"/>
            if <paramref name="cancellationToken"/> is signaled (the part file and state are
            left intact for resume). Throws <see cref="T:PDM.Core.Downloading.DownloadException"/> on fatal errors.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.CollapseToSingleStream">
            <summary>
            Rewrites the plan into a single, whole-file stream starting at byte 0 and marks the download as
            non-resumable-by-range. Used as the automatic fallback when a server will not honour the range
            requests our segmented plan requires (it returned a full <c>200</c> body, a range starting at the
            wrong offset, or the content changed under an <c>If-Range</c> validator). Any partial bytes from
            the failed attempt are discarded because they cannot be trusted; the single stream simply
            overwrites the part file sequentially from the beginning.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.TryClaimWork">
             <summary>
             Hands the calling connection its next piece of work, or null when the download has none left.
             This is the heart of the work-stealing scheduler and the fix for the "last 10% runs on one
             connection" problem.
            
             <para>Two strategies, in order:</para>
             <list type="number">
               <item><b>Claim an unstarted segment.</b> Cheap and always preferred — no extra HTTP request
                     beyond the one this connection was going to make anyway.</item>
               <item><b>Split the busiest in-flight segment.</b> The segment with the most bytes still
                     outstanding is cut in half: its end is moved back and the freed connection takes the
                     tail. Repeated as connections free up, so parallelism is sustained to the end of the
                     file instead of decaying as segments finish.</item>
             </list>
            
             <para><b>Why splitting is safe.</b> A segment's end only ever moves backwards, and never closer
             than a safety margin ahead of the bytes that segment has already written. The margin is taken
             from the <em>live</em> counter (not the durable one, which lags behind between flushes) plus the
             size of a read that may be in flight. The owning connection re-reads its volatile end each loop
             iteration and stops cleanly at the new boundary, so every downloaded byte is preserved exactly
             once and the two halves remain a perfect, non-overlapping cover of the range.</para>
            
             <para>Splitting is skipped entirely for non-resumable (single-stream) and unknown-size
             downloads, since neither can address a byte range.</para>
             </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ReleaseSegment(PDM.Core.Models.DownloadSegment)">
            <summary>
            Returns a segment to the unclaimed pool (keeping its downloaded bytes) so another connection,
            or a later lower-concurrency round, can pick it up. Called when a connection retires.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ComputeSplitFloor(System.Collections.Generic.List{PDM.Core.Models.DownloadSegment})">
             <summary>
             Minimum bytes a split must hand over to be worthwhile. Takes the larger of the configured
             absolute floor and the amount the observed per-connection throughput would move in
             <see cref="P:PDM.Core.Models.DownloadOptions.MinSplitDuration"/>.
            
             <para>This is what makes the scheduler behave correctly across very different links. On a
             10 Mbps connection a few megabytes keep a connection busy for many seconds, so the absolute
             floor dominates and splitting stays aggressive. At 1 Gbps the same few megabytes transfer in a
             fraction of a second, so the floor rises into the tens of megabytes and PDM stops splitting
             well before per-request latency would outweigh the benefit.</para>
            
             <para>Must be called while holding <c>_planLock</c>.</para>
             </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.PreparePartFile">
            <summary>
            Creates or opens the part file and preallocates it to the known total size to reduce
            fragmentation. Marks the file sparse first (Windows) so preallocation + scattered multi-segment
            writes do not trigger a whole-file NTFS zero-fill that would saturate throughput-limited storage
            and stall the network connections. Preallocation is skipped when the total size is unknown.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ReadBytesOf(PDM.Core.Models.DownloadSegment)">
            <summary>Bytes read so far for a segment (the read cursor; may run ahead of the written offset).</summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.IsReadComplete(PDM.Core.Models.DownloadSegment)">
            <summary>True when every byte of the segment's range has been read from the network.</summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ApplyRateLimitsAsync(System.Int32,System.Threading.CancellationToken)">
            <summary>Applies the per-download cap and, when present, the shared global cap after a read.</summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.OnDurableAdvanced(System.Int32,System.Int64)">
            <summary>
            Flushes the FileStream's own buffer to the operating system without forcing a hardware sync.
            Cheap enough for the hot path and sufficient to make an advanced resume offset safe against a
            process crash (the bytes live in the OS page cache). Storage failures become a disk-error.
            </summary>
            <summary>
            Advances a segment's durable (written-to-disk) offset. Invoked by the DiskWriteQueue once a
            segment's <em>contiguous</em> written prefix grows, so the value is always a length that is
            genuinely safe to resume from even though several writes may be in flight out of order. This is
            the value persisted for resume and checked by verification — distinct from the read cursor
            (_liveBytes), which runs ahead while data is buffered for writing.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.FinalizeAsync(System.Diagnostics.Stopwatch,System.Threading.CancellationToken)">
            <summary>
            Forces the completed part file all the way to physical disk exactly once, at finalize. This is
            the single hardware sync per download (the hot path uses cheap async buffer flushes only).
            Best-effort: a failure here does not corrupt anything — the data is already in the OS cache — so
            it must not block delivery of an otherwise-verified file.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.VerifyTransferCompleteness">
             <summary>
             Proves the transfer actually covered every byte of the file, returning null when everything
             checks out or a human-readable description of the first problem found.
            
             <para>This is the guard that makes a silently-incomplete download impossible. Because the part
             file is preallocated to its final size, a length comparison proves nothing; what matters is
             whether the segments account for the whole file:</para>
             <list type="number">
               <item><b>Every segment complete</b> — no connection stopped early.</item>
               <item><b>Ranges tile the file exactly</b> — sorted by start offset they must be perfectly
                     contiguous from byte 0 to the last byte, with no gap (a gap would be a zero-filled hole
                     in the output) and no overlap (an overlap means some region was never claimed).</item>
               <item><b>Accounted bytes equal the file size</b> — a final independent cross-check of the
                     per-segment counters against the expected total.</item>
             </list>
             Together these catch any plan or scheduling defect that would otherwise produce a corrupt file,
             including a bad work-stealing split.
             </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.VerifyContentDigestAsync(System.Threading.CancellationToken)">
             <summary>
             Hashes the completed part file and compares it against the digest the server advertised, so a
             file that is the right size but the wrong content is caught rather than delivered.
            
             <para><b>Optional by design.</b> Every step is a no-op when the information is not available:
             no digest advertised, an algorithm PDM cannot compute, or verification disabled via
             <see cref="P:PDM.Core.Models.DownloadOptions.VerifyContentDigest"/>. In all those cases the method simply returns
             and the download completes on byte accounting alone. A missing digest is never an error.</para>
            
             <para>A genuine mismatch means the bytes are wrong, so the file is not delivered: the plan is
             reset and the download is failed with a clear message, exactly like a completeness failure.</para>
             </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ResetPlanForCleanRetry">
            <summary>
            Discards the current plan and rebuilds a fresh one from byte 0, so a download that failed
            verification restarts cleanly instead of resuming on top of data we could not prove correct.
            Correctness is preferred over saving the partial transfer here: delivering a corrupt file is a
            far worse outcome than re-downloading.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.SetIssue(PDM.Core.Models.DownloadIssue,System.Int32)">
            <summary>Records the current stall/slowdown cause for the next progress snapshot.</summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ClearIssue">
            <summary>Clears the diagnostic hint once a connection is healthy / making progress.</summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ClassifyIssue(System.Exception)">
            <summary>
            Maps a transient failure to the most likely user-facing cause. Checks overall connectivity
            first (a dropped link explains everything), then the exception shape.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.SumDurable">
            <summary>
            Total bytes actually written to disk so far (durable offset). Unlike <see cref="M:PDM.Core.Downloading.DownloadWorker.SumLive"/>
            (bytes read off the socket, which the write buffer inflates during bursts), this reflects the
            true sustained goodput and is the signal the connection-probing controller measures.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ProbeConnectionsAsync(System.Int32,System.Int32,System.Collections.Generic.List{System.Threading.Tasks.Task},System.Func{System.Threading.Tasks.Task},System.Threading.CancellationToken)">
            <summary>
            Adaptive connection controller. Starting from <paramref name="current"/> connections, adds one
            at a time and keeps it only while it raises the measured durable throughput by at least
            <see cref="F:PDM.Core.Downloading.DownloadWorker.ThroughputGainThreshold"/>. Stops as soon as an added connection fails to help (link,
            disk, or server saturated) or the server starts refusing connections. This is what makes the
            connection count adapt to each server/network instead of being fixed.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.SnapshotForCheckpoint">
            <summary>
            Produces a point-in-time copy of the state for a durable checkpoint. Copies each segment's
            persisted <see cref="P:PDM.Core.Models.DownloadSegment.BytesDownloaded"/> offset (a single atomic long read) into
            a fresh list so the write to disk reflects a consistent snapshot even while segments advance.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.AddIfRangeHeader(System.Net.Http.HttpRequestMessage)">
            <summary>
            Adds an <c>If-Range</c> conditional to a resumable request so the server only serves the
            requested byte range while the content is unchanged. Per RFC 7233 an <c>If-Range</c> must not
            carry a weak validator, so a weak/unparseable ETag is skipped in favour of Last-Modified.
            When neither validator is available the header is omitted (no protection possible).
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.IsWebPageResponse(System.Net.Http.HttpResponseMessage)">
            <summary>
            True when the response is an HTML/XHTML page rather than file content. Used to detect that a
            download link has started serving an interstitial/error page (expired token, quota, or a
            sign-in requirement) instead of the file we validated at prepare time.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.DownloadWorker.RangeOutcome">
            <summary>What the caller should do with the response body of a (possibly) ranged request.</summary>
        </member>
        <member name="F:PDM.Core.Downloading.DownloadWorker.RangeOutcome.WriteAtOffset">
            <summary>Write the body at the current write offset (206 verified, or a non-ranged request).</summary>
        </member>
        <member name="F:PDM.Core.Downloading.DownloadWorker.RangeOutcome.RestartFromZero">
            <summary>Single-segment download: the server sent the whole file, so write it from byte 0.</summary>
        </member>
        <member name="M:PDM.Core.Downloading.DownloadWorker.ResolveRangeResponse(System.Net.Http.HttpResponseMessage,PDM.Core.Models.DownloadSegment,System.Boolean,System.Int64)">
            <summary>
            Reconciles a response with the range we requested, protecting against the silent-corruption
            cases while keeping downloads working against servers that don't truly support ranges:
            <list type="bullet">
              <item>a server that ignores the <c>Range</c> header and returns <c>200</c> with the whole
                    file, which must never be written at a non-zero offset; and</item>
              <item>a resource whose contents changed under our <c>If-Range</c> validator, which also
                    answers <c>200</c> and would otherwise splice two different files together.</item>
            </list>
            <para>
            The response to these cases depends on the plan shape:
            </para>
            <list type="bullet">
              <item><b>Single segment</b> (one writer): a full <c>200</c> — or a <c>206</c> starting at an
                    unexpected offset — is handled safely by (re)starting the write from byte 0
                    (<see cref="F:PDM.Core.Downloading.DownloadWorker.RangeOutcome.RestartFromZero"/>). No corruption is possible because there is
                    only one writer covering the whole file.</item>
              <item><b>Multiple segments</b>: the same situation cannot be written safely (bytes would land
                    at the wrong offset), so it throws <see cref="T:PDM.Core.Downloading.DownloadWorker.RangeNotHonoredException"/>, which
                    <see cref="M:PDM.Core.Downloading.DownloadWorker.RunAsync(System.Threading.CancellationToken)"/> catches to collapse the whole download to a single stream.</item>
            </list>
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.DownloadWorker.RangeNotHonoredException">
            <summary>
            Internal control-flow signal (not surfaced to callers) meaning "the server would not honour the
            ranged request this segmented plan needs". <see cref="M:PDM.Core.Downloading.DownloadWorker.RunAsync(System.Threading.CancellationToken)"/> catches it and re-downloads the
            whole file as a single stream from byte 0. It is deliberately NOT a <see cref="T:PDM.Core.Downloading.DownloadException"/>
            and NOT transient, so it bypasses both per-segment retries and the fatal-error path.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.DownloadWorker.SegmentUnavailableException">
            <summary>
            Internal signal that a single connection exhausted its retries for a segment. It is NOT fatal:
            the worker loop catches it, releases the segment, and retires just that connection, so the
            download keeps going on the remaining connections instead of failing outright.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.LikelyWebPageException">
            <summary>
            Thrown by <see cref="M:PDM.Core.Downloading.DownloadEngine.PrepareAsync(System.Uri,System.String,System.String,System.Nullable{PDM.Core.Models.DownloadCategory},PDM.Core.Models.OverwritePolicy,System.Boolean,PDM.Core.Models.DownloadOptions,System.String,System.Threading.CancellationToken)"/> when the given URL points at an HTML
            page rather than a downloadable file. The UI catches this and offers the user the choice
            to install the browser extension (which captures the actual media URLs behind the page)
            or to override and download the page's HTML source anyway.
            </summary>
        </member>
        <member name="P:PDM.Core.Downloading.LikelyWebPageException.Url">
            <summary>The URL that was detected as a web page.</summary>
        </member>
        <member name="P:PDM.Core.Downloading.LikelyWebPageException.ContentType">
            <summary>The <c>Content-Type</c> the server reported.</summary>
        </member>
        <member name="T:PDM.Core.Downloading.SegmentPlanner">
            <summary>
            Computes the segmentation plan for a download: how many parallel connections to
            use and the exact byte range each one owns. The connection count scales with file
            size but never exceeds the configured maximum, and never produces segments smaller
            than <see cref="P:PDM.Core.Models.DownloadOptions.MinSegmentSize"/>.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.SegmentPlanner.Plan(System.Nullable{System.Int64},System.Boolean,PDM.Core.Models.DownloadOptions)">
            <summary>
            Builds the list of segments for a download of <paramref name="totalBytes"/>.
            When ranges are unsupported or the size is unknown, a single segment spanning
            the whole file is returned (the download then runs as a single stream).
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.SegmentPlanner.ReplanRemaining(System.Collections.Generic.IReadOnlyList{PDM.Core.Models.DownloadSegment},PDM.Core.Models.DownloadOptions)">
             <summary>
             Rebuilds the segment list for a <em>resuming</em> download so the bytes that are still missing
             are spread back across many parallel connections. Static segmentation means a paused download
             resumes only on its still-incomplete segments; as those finish they are not replaced, so a
             resumed transfer can crawl on one or two connections while a fresh download of the same file
             runs on the full connection count. This re-splits only the not-yet-downloaded ranges,
             preserving every downloaded byte exactly (already-downloaded ranges become pre-completed
             segments the worker skips).
            
             <para>Returns null when re-planning would not help or is unsafe: nothing downloaded yet,
             everything already downloaded, or the remaining ranges are already spread across at least
             <see cref="P:PDM.Core.Models.DownloadOptions.MaxConnections"/> segments. Only meaningful for range-capable,
             known-size downloads — callers should not use it otherwise.</para>
             </summary>
        </member>
        <member name="M:PDM.Core.Downloading.SegmentPlanner.DetermineConnectionCount(System.Int64,PDM.Core.Models.DownloadOptions)">
            <summary>
            Chooses the connection count so each segment is at least the minimum size,
            capped at <see cref="P:PDM.Core.Models.DownloadOptions.MaxConnections"/>.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.SpeedLimiter">
            <summary>
            A thread-safe token-bucket rate limiter. A single instance can be shared across the segments of
            one download <em>and</em> across many concurrent downloads to enforce a single aggregate cap —
            callers invoke <see cref="M:PDM.Core.Downloading.SpeedLimiter.ThrottleAsync(System.Int32,System.Threading.CancellationToken)"/> after each read and the call asynchronously delays to
            smooth transfers to the configured rate. The cap can be changed at runtime via
            <see cref="M:PDM.Core.Downloading.SpeedLimiter.SetBytesPerSecond(System.Int64)"/> (used to track a changing global speed setting without recreating
            the limiter). A limit of zero disables throttling entirely: the fast path takes no lock, so an
            unlimited limiter adds no measurable overhead.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.SpeedLimiter.#ctor(System.Int64)">
            <summary>
            Creates a limiter capped at <paramref name="bytesPerSecond"/>. A value of zero or less means
            unlimited (throttling disabled).
            </summary>
        </member>
        <member name="P:PDM.Core.Downloading.SpeedLimiter.IsEnabled">
            <summary>True when this limiter currently enforces a cap.</summary>
        </member>
        <member name="M:PDM.Core.Downloading.SpeedLimiter.SetBytesPerSecond(System.Int64)">
            <summary>
            Updates the cap at runtime. Zero or less disables throttling. Shared instances use this to
            follow a changing global speed setting without being recreated. The token bucket is reset so a
            rate change (or a re-enable after being idle) never releases a large accumulated burst.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.SpeedLimiter.ThrottleAsync(System.Int32,System.Threading.CancellationToken)">
            <summary>
            Accounts for <paramref name="byteCount"/> transferred bytes and, if the bucket
            is exhausted, asynchronously waits until enough capacity has accrued.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.UrlChangeCompatibility">
            <summary>
            How a replacement URL relates to an existing (paused/failed) download.
            </summary>
        </member>
        <member name="F:PDM.Core.Downloading.UrlChangeCompatibility.FreshStart">
            <summary>Nothing has been transferred yet, so the new URL can simply be used from the start.</summary>
        </member>
        <member name="F:PDM.Core.Downloading.UrlChangeCompatibility.ResumeSafe">
            <summary>
            Partial data exists AND the new URL is confidently the same, resumable file, so the
            transfer can continue from its current offsets without corruption.
            </summary>
        </member>
        <member name="F:PDM.Core.Downloading.UrlChangeCompatibility.RestartRequired">
            <summary>
            Partial data exists but the new URL is a different or changed resource, or is not
            resumable. Continuing would corrupt the file, so the download must be restarted from
            zero if the user wants to use this URL.
            </summary>
        </member>
        <member name="T:PDM.Core.Downloading.UrlChangeAssessment">
            <summary>
            The result of comparing an existing <see cref="T:PDM.Core.Models.DownloadState"/> against a freshly probed
            replacement URL. <see cref="P:PDM.Core.Downloading.UrlChangeAssessment.Reason"/> is a human-readable explanation suitable for showing
            to the user in the change-URL dialog.
            </summary>
        </member>
        <member name="M:PDM.Core.Downloading.UrlChangeAssessment.#ctor(PDM.Core.Downloading.UrlChangeCompatibility,System.String,System.Boolean,System.Boolean,System.Boolean)">
            <summary>
            The result of comparing an existing <see cref="T:PDM.Core.Models.DownloadState"/> against a freshly probed
            replacement URL. <see cref="P:PDM.Core.Downloading.UrlChangeAssessment.Reason"/> is a human-readable explanation suitable for showing
            to the user in the change-URL dialog.
            </summary>
        </member>
        <member name="P:PDM.Core.Downloading.UrlChangeAssessment.CanApplyWithoutRestart">
            <summary>True when the new URL can be applied without discarding progress.</summary>
        </member>
        <member name="T:PDM.Core.Downloading.UrlChangeEvaluator">
             <summary>
             Decides whether a replacement download URL can be applied to an in-progress download while
             preserving the bytes already written. This is the safety gate for the "change/refresh URL"
             feature: resuming a partially-downloaded file against a URL that serves <em>different</em>
             bytes would silently corrupt the output (segment offsets would be filled from two different
             files), so anything short of high confidence that it is the same, resumable file forces a
             restart.
            
             <para>The logic is intentionally pure (no I/O) so it is exhaustively unit-testable.</para>
             </summary>
        </member>
        <member name="M:PDM.Core.Downloading.UrlChangeEvaluator.Evaluate(PDM.Core.Models.DownloadState,PDM.Core.Models.RemoteFileInfo)">
            <summary>
            Compares <paramref name="state"/> (what we have on disk) with <paramref name="newInfo"/>
            (a fresh probe of the candidate URL) and returns the safest applicable outcome.
            </summary>
        </member>
        <member name="T:PDM.Core.Models.AppSettings">
            <summary>
            Application-wide user settings, persisted as JSON. Controls default download behavior,
            UI preferences, and the manager-level queue/scheduler.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.SettingsVersion">
            <summary>
            Schema version of the persisted settings file, used to apply one-time migrations when defaults
            change. 0 means the file predates versioning. See <c>JsonSettingsStore.Migrate</c>.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.DefaultDownloadDirectory">
            <summary>Directory used when a download does not specify one.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.CategoryFolders">
            <summary>Per-category folders (relative to <see cref="P:PDM.Core.Models.AppSettings.DefaultDownloadDirectory"/> or absolute).</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.MaxSimultaneousDownloads">
            <summary>Maximum number of downloads that may transfer at the same time.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.MaxConnectionsPerDownload">
            <summary>
            Default per-download connection count. 8 is the stable default established managers (e.g. IDM)
            use: it sits under the per-IP connection limit most servers enforce, so it delivers full speed
            without triggering the refusals/throttling and retry churn that higher counts cause. Users can
            raise it for servers that reward more connections.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.GlobalMaxBytesPerSecond">
            <summary>Global speed cap in bytes/sec across all downloads; 0 = unlimited.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.UserAgent">
            <summary>User-Agent header used for outgoing requests; null uses the default.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.AutoStartAddedDownloads">
            <summary>When true, add downloads directly to the running queue; otherwise leave them Queued only.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.ProxyUrl">
            <summary>Optional proxy URL (e.g. http://user:pass@host:8080). Null uses system default / none.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.Theme">
            <summary>Theme preference: "system", "light", or "dark".</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.AccentColor">
            <summary>Accent colour id: "blue", "purple", "green", "orange", "pink", "red", or "teal".</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.ShowNotifications">
            <summary>Show desktop notifications on completion/failure.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.ScheduleStart">
            <summary>Scheduled quiet-hours start-time (local, 24h HH:mm); null disables the schedule.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.ScheduleEnd">
            <summary>Scheduled quiet-hours end-time (local, 24h HH:mm); null disables the schedule.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.UpdateChannel">
            <summary>Release channel to subscribe to for auto-updates ("Stable" or "Beta").</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.UpdateManifestUrl">
            <summary>Manifest URL for auto-update checks; null disables updates.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.UpdatePublicKeyBase64">
            <summary>Base64 SPKI of the ECDSA P-256 public key used to verify update signatures.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.UpdateVersionFloor">
            <summary>
            Highest update version this install has ever been offered by a valid signed manifest
            (anti-rollback / freeze defence, M4). A later fetch of a validly-signed but <i>older</i>
            manifest — e.g. a MITM replaying a stale release to suppress a security update — is rejected
            when its version is below this floor. Stored as a version string; empty until the first check.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.OverwritePolicy">
            <summary>How to handle name collisions when the destination file already exists.</summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.ConfirmBrowserDownloads">
            <summary>
            When true (the default), browser-captured downloads are held for user confirmation
            before they start, so an accidental click in the browser can't kick off an unwanted
            download. Manual Add Download / Add Many entries are never gated.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.PostDownloadCommand">
            <summary>
            Optional command (executable path) invoked after a download completes; the command
            is called with the destination path as the sole argument. Common uses: virus scan,
            checksum verification, custom processing. Null disables the hook.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.PreferredBrowserPath">
            <summary>
            Full path to the browser executable the user last chose via the toolbar "Open browser" button.
            Remembered so future clicks reuse it. Null means no preference yet (use the first detected
            browser).
            </summary>
        </member>
        <member name="P:PDM.Core.Models.AppSettings.LicenseExpiryWarningDays">
            <summary>
            How many days before a time-limited license expires the main window begins showing the
            expiration warning bar. Default 7; set to 0 to only warn on the final day.
            </summary>
        </member>
        <member name="M:PDM.Core.Models.AppSettings.ResolveCategoryFolder(PDM.Core.Models.DownloadCategory)">
            <summary>Resolves the destination directory for the given category.</summary>
        </member>
        <member name="T:PDM.Core.Models.DownloadCategory">
            <summary>
            Built-in categories that PDM uses to organize downloads into folders and filter the
            library UI. Custom user-defined categories map onto <see cref="F:PDM.Core.Models.DownloadCategory.Custom"/> combined
            with a category name string stored alongside the download.
            </summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadCategory.General">
            <summary>Everything not classified elsewhere.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadCategory.Documents">
            <summary>Documents (pdf, docx, xlsx, txt, ...).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadCategory.Compressed">
            <summary>Compressed archives (zip, 7z, rar, tar, ...).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadCategory.Music">
            <summary>Music files (mp3, flac, wav, ogg, m4a, ...).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadCategory.Video">
            <summary>Video files (mp4, mkv, mov, webm, ...).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadCategory.Programs">
            <summary>Programs and installers (exe, msi, ...).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadCategory.Custom">
            <summary>User-defined category.</summary>
        </member>
        <member name="T:PDM.Core.Models.DownloadIssue">
            <summary>
            The most likely cause of a download stalling or slowing, detected by the worker so the UI can show
            a clear, human-readable status instead of a generic "stuck" state. <see cref="F:PDM.Core.Models.DownloadIssue.None"/> means the
            transfer is healthy (or not running). These are transient hints carried on a progress snapshot;
            they are not persisted.
            </summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadIssue.None">
            <summary>No problem detected — the transfer is proceeding normally.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadIssue.NoInternet">
            <summary>The machine has no network connectivity at all.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadIssue.ServerNotResponding">
            <summary>The server is reachable but not responding (5xx / connection refused / no reply).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadIssue.ConnectionTimedOut">
            <summary>A request timed out waiting for the server.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadIssue.NetworkUnstable">
            <summary>The connection keeps dropping mid-transfer; throughput is degraded.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadIssue.DiskError">
            <summary>Data could not be written to disk (out of space, permissions, or I/O failure).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadIssue.Retrying">
            <summary>A transient failure is being retried and the cause is not more specific.</summary>
        </member>
        <member name="T:PDM.Core.Models.DownloadOptions">
            <summary>
            Tunable parameters that control how a single download is executed.
            Defaults are chosen to be safe and fast on typical broadband connections.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.MaxConnections">
            <summary>
            Maximum number of parallel connections (segments) for one download — the ceiling the adaptive
            scheduler is allowed to ramp <em>up</em> to. Defaults to 8, the same stable count established
            managers (e.g. IDM) use by default, because it is comfortably under the per-IP connection limit
            most servers enforce. Going higher tends to trigger refusals/throttling and retry-backoff churn
            that makes downloads slower and jumpy, not faster. Advanced users can raise this; the scheduler
            then ramps toward it only while extra connections measurably increase throughput.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.InitialConnections">
            <summary>
            Number of connections a download opens at the start, before adaptive probing. Kept low (2) so a
            download never blasts a server it hasn't measured yet: the scheduler then adds connections one
            at a time, keeping only those that measurably raise real throughput. This converges on each
            server's optimal count — climbing where parallelism helps, staying at 2 where the server
            throttles or the link/disk is already saturated (the profile that keeps speed stable instead of
            collapsing partway through, and that matches how IDM parks unneeded connections).
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.RampUpInterval">
            <summary>How long to observe throughput at each step of adaptive connection probing.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.MinSegmentSize">
            <summary>
            Minimum bytes a segment must span. Prevents spawning many tiny connections for
            small files where the per-connection overhead outweighs the benefit.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.MaxBytesPerSecond">
            <summary>Per-download speed cap in bytes per second; <c>0</c> means unlimited.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.ReadBufferSize">
            <summary>
            Size of the buffer used for each socket read, in bytes — and therefore the size of each disk
            write. 512 KiB keeps read syscalls and queue operations low on very fast links, and makes each
            disk write large enough to reach good throughput per I/O (write throughput is roughly
            chunk-size ÷ latency, so small chunks cap the writer). Kept at or below the shared array pool's
            1 MiB bucket so buffers are pooled rather than allocated.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.DiskWriteParallelism">
            <summary>
            How many disk writes may be in flight at once. A single in-flight write limits throughput to
            chunk-size ÷ write-latency (roughly 100-250 MB/s on typical storage), which on a fast link makes
            the writer — not the network — the bottleneck and causes the bounded buffer to fill and stall
            every connection. Issuing several writes concurrently raises the storage queue depth so fast
            NVMe/premium disks can be saturated. Writes target non-overlapping offsets, and the durable
            resume offset is tracked contiguously, so parallel writes remain safe.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.MinSplitSize">
            <summary>
            Absolute lower bound on the number of bytes a work-stealing split may hand to a freed
            connection. Prevents spending an HTTP round trip on a scrap of data. See
            <see cref="P:PDM.Core.Models.DownloadOptions.MinSplitDuration"/> for the bandwidth-aware component of the same decision.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.MinSplitDuration">
             <summary>
             Bandwidth-aware component of the work-stealing split threshold: a split is only worthwhile if
             the claimed half would keep a connection busy for at least this long at the currently observed
             per-connection throughput.
            
             <para>This is what makes segment splitting correct across wildly different link speeds. On a
             10 Mbps line the effective floor stays at <see cref="P:PDM.Core.Models.DownloadOptions.MinSplitSize"/>; at 1 Gbps it rises to
             tens of megabytes, so PDM stops splitting long before the per-request latency would cost more
             than the parallelism gains. Without it, a fixed small chunk size would add substantial dead
             time on fast, high-latency connections.</para>
             </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.MaxRetriesPerSegment">
            <summary>Number of automatic retry attempts per segment before failing.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.RetryBaseDelay">
            <summary>Base delay for exponential backoff between retries.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.RetryMaxDelay">
            <summary>
            Upper bound on the backoff delay between retries. Kept modest (10s) so a connection the server
            is refusing does not sit idle for tens of seconds before retiring — long backoff on refused
            connections was the dominant source of "waiting for server" dead-time on short downloads.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.ProgressInterval">
            <summary>How often progress snapshots are emitted to observers.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.ConnectTimeout">
            <summary>Timeout for establishing a connection and receiving response headers.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.StallTimeout">
            <summary>
            Maximum time a single segment may wait for the next chunk of data before the connection is
            treated as stalled and retried from its last persisted offset. Guards against a socket that
            stays open but stops delivering bytes, which would otherwise hang the whole download.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.UserAgent">
            <summary>Custom User-Agent header; a sensible default is used when null.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.MaxBufferedBytes">
            <summary>
            Maximum bytes buffered in memory between the network readers and the disk writer. This buffer is
            what smooths a bursty/throughput-limited disk into a steady network speed: connections keep
            reading into it while the writer drains to disk. Larger absorbs longer disk stalls at the cost of
            RAM; the default (128 MiB) covers a couple of seconds of a fast link. Shared across all of a
            download's connections.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadOptions.VerifyContentDigest">
             <summary>
             When true (the default), a completed file is hashed and compared against the digest the server
             advertised (<c>Repr-Digest</c>, <c>Digest</c>, or <c>Content-MD5</c>) before being delivered.
            
             <para>This only costs anything when a server actually publishes a digest, which is uncommon; with
             no digest available the download completes exactly as before. Set to false to skip the hash pass
             even when a digest is offered (e.g. to save time on very large files over slow storage).</para>
             </summary>
        </member>
        <member name="M:PDM.Core.Models.DownloadOptions.Validate">
            <summary>Validates the option values, throwing when a value is out of range.</summary>
        </member>
        <member name="T:PDM.Core.Models.DownloadProgress">
            <summary>
            An immutable snapshot of a download's progress, suitable for reporting to a UI.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.BytesDownloaded">
            <summary>Bytes transferred so far across all segments.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.TotalBytes">
            <summary>Total bytes to transfer, or <c>null</c> when the size is unknown.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.BytesPerSecond">
            <summary>Instantaneous transfer rate in bytes per second (smoothed).</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.AverageBytesPerSecond">
            <summary>Average transfer rate since the download started, in bytes per second.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.ActiveConnections">
            <summary>Number of segments currently transferring data.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.TotalConnections">
            <summary>Total number of segments for this download.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.Status">
            <summary>Current lifecycle status.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.Issue">
            <summary>
            The most likely cause of a stall/slowdown while running, or <see cref="F:PDM.Core.Models.DownloadIssue.None"/>
            when healthy. Lets the UI explain <em>why</em> a download paused or slowed.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.RetryAttempt">
            <summary>Current retry attempt for the affected connection (0 when not retrying).</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.MaxRetries">
            <summary>Maximum retry attempts before a connection is considered failed.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.Fraction">
            <summary>Completion fraction in the range [0, 1], or <c>null</c> when size is unknown.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadProgress.Eta">
            <summary>Estimated time remaining, or <c>null</c> when it cannot be computed.</summary>
        </member>
        <member name="T:PDM.Core.Models.DownloadRequest">
            <summary>
            A download capture request handed to PDM by the browser integration (via the native
            messaging host and local IPC). Kept small and transport-agnostic.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadRequest.Url">
            <summary>The absolute URL to download.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadRequest.Referrer">
            <summary>Optional referrer page, for servers that require it.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadRequest.FileName">
            <summary>Optional suggested file name from the browser.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadRequest.Directory">
            <summary>Optional destination directory override.</summary>
        </member>
        <member name="T:PDM.Core.Models.DownloadSegment">
            <summary>
            A contiguous byte range of the target file assigned to a single connection.
            <see cref="P:PDM.Core.Models.DownloadSegment.BytesDownloaded"/> advances as data is written so the segment can
            resume from where it left off after a pause, restart, or crash.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadSegment.Index">
            <summary>Zero-based index of this segment within the download.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadSegment.Start">
            <summary>Absolute start offset (inclusive) of this segment in the output file.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadSegment.End">
             <summary>
             Absolute end offset (inclusive) of this segment in the output file.
            
             <para><b>Concurrency:</b> this value is read and written with volatile semantics because the
             work-stealing scheduler may <em>shrink</em> a live segment's end while its own connection is
             still transferring (see <c>DownloadWorker.TryClaimWork</c>). The owning connection re-reads it
             on every loop iteration and simply stops at the new boundary. The end is only ever moved
             backwards, and never below the bytes already written plus a safety margin, so no downloaded
             byte is ever discarded or duplicated.</para>
             </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadSegment.BytesDownloaded">
            <summary>Number of bytes already written for this segment.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadSegment.Length">
            <summary>Total number of bytes this segment is responsible for.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadSegment.Remaining">
            <summary>Remaining bytes to transfer for this segment.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadSegment.CurrentOffset">
            <summary>The absolute file offset at which the next write should occur.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadSegment.IsComplete">
            <summary>True when the entire segment has been transferred.</summary>
        </member>
        <member name="T:PDM.Core.Models.DownloadState">
            <summary>
            Serializable state for a download, persisted to a sidecar file so transfers can
            resume after a pause, application restart, or crash. Kept intentionally simple
            and self-contained so it can be written atomically as JSON.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.Id">
            <summary>Stable identifier for the download.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.SourceUrl">
            <summary>The originally requested URL.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.EffectiveUrl">
            <summary>The effective URL after redirects (used for resuming segments).</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.Referrer">
            <summary>
            Optional referrer page the download was initiated from. Sent as the <c>Referer</c> header
            on both the inspection probe and every segment request so downloads that a server only
            serves when the request carries a referrer (a very common hot-link protection) succeed
            exactly as they did in the browser. Persisted so a resumed transfer keeps sending it.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.DestinationPath">
            <summary>Absolute path to the final output file.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.TotalBytes">
            <summary>Total size in bytes, or <c>null</c> when unknown.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.SupportsRanges">
            <summary>Whether the server supported range requests when the plan was created.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.AllowWebPage">
            <summary>
            True when the user explicitly chose to download this URL even though it is a web page (HTML).
            Persisted so the transfer does not later reject an HTML response as an expired/interstitial
            link — that guard only applies to downloads that were expected to be real files.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.ETag">
            <summary>Server ETag captured at plan time; used to detect content changes.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.LastModified">
            <summary>Server Last-Modified captured at plan time; a weak resume validator.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.ExpectedDigestAlgorithm">
            <summary>
            Digest algorithm the server advertised for this content (e.g. <c>SHA-256</c>), or null when it
            advertised none. Persisted so verification still happens after a resume or app restart.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.ExpectedDigestValue">
            <summary>
            Expected content digest as base64, paired with <see cref="P:PDM.Core.Models.DownloadState.ExpectedDigestAlgorithm"/>. When both
            are set, the completed file is hashed and must match before it is delivered. When null (the
            common case, since most servers publish no digest) the download completes on byte accounting
            alone.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.Status">
            <summary>Current lifecycle status.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.Category">
            <summary>Category this download belongs to.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.CustomCategory">
            <summary>Optional user-defined category label when <see cref="P:PDM.Core.Models.DownloadState.Category"/> is Custom.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.ErrorMessage">
            <summary>Human-readable error message if the download failed.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.CompletedUtc">
            <summary>UTC timestamp when the download reached a terminal state.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.Segments">
            <summary>The segments that make up this download.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.CreatedUtc">
            <summary>UTC timestamp when the download was created.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.LastAttemptUtc">
            <summary>
            UTC timestamp of the most recent transfer attempt. Used to judge whether a stored download URL
            is likely stale: a download paused and resumed moments later almost certainly still has a valid
            link, whereas one left for hours may have an expired signed/CDN URL. This lets a resume skip an
            unnecessary re-probe (a network round trip, and one more request against hosts that rate-limit).
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.LastAttemptFailed">
            <summary>
            True when the most recent transfer attempt ended in failure. Kept separate from
            <see cref="P:PDM.Core.Models.DownloadState.ErrorMessage"/> because resuming clears the user-facing error text before the next
            attempt starts, yet the fact that the last try failed is exactly what tells the engine the stored
            URL is a prime suspect and should be re-probed. Cleared once an attempt completes successfully.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.BytesDownloaded">
            <summary>Total bytes transferred across all segments.</summary>
        </member>
        <member name="P:PDM.Core.Models.DownloadState.AllSegmentsComplete">
            <summary>True when every segment has finished transferring.</summary>
        </member>
        <member name="T:PDM.Core.Models.DownloadStatus">
            <summary>
            Lifecycle states for a download. Ordering is not significant.
            </summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Queued">
            <summary>Created but not yet started; waiting in the queue.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Connecting">
            <summary>Contacting the server and probing capabilities (size, ranges).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Downloading">
            <summary>Actively transferring bytes.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Paused">
            <summary>Paused by the user; can be resumed.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Assembling">
            <summary>Segments transferred; assembling/finalizing the output file.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Verifying">
            <summary>Verifying integrity (size and optional checksum).</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Completed">
            <summary>Finished successfully and verified.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Failed">
            <summary>Stopped due to an unrecoverable error.</summary>
        </member>
        <member name="F:PDM.Core.Models.DownloadStatus.Canceled">
            <summary>Canceled by the user; partial data may be discarded.</summary>
        </member>
        <member name="T:PDM.Core.Models.OverwritePolicy">
            <summary>
            Controls what happens when a download resolves to a destination path that already exists.
            </summary>
        </member>
        <member name="F:PDM.Core.Models.OverwritePolicy.Rename">
            <summary>Append " (N)" to the name until it is unique. Safest default.</summary>
        </member>
        <member name="F:PDM.Core.Models.OverwritePolicy.Overwrite">
            <summary>Overwrite the existing file at the destination.</summary>
        </member>
        <member name="F:PDM.Core.Models.OverwritePolicy.Skip">
            <summary>Refuse the download; caller will see an error.</summary>
        </member>
        <member name="T:PDM.Core.Models.RemoteFileInfo">
            <summary>
            Metadata about a remote resource discovered during probing. Used to plan the
            download strategy (segmented vs. single-stream) and to validate resume safety.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.EffectiveUrl">
            <summary>The final URL after following redirects.</summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.TotalBytes">
            <summary>Total content length in bytes, or <c>null</c> if the server did not report it.</summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.SupportsRanges">
            <summary>True when the server advertises support for HTTP range requests.</summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.SuggestedFileName">
            <summary>Suggested file name derived from Content-Disposition or the URL path.</summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.ContentType">
            <summary>MIME type reported by the server, if any.</summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.ETag">
            <summary>Entity tag used to detect server-side changes between sessions.</summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.LastModified">
            <summary>Last-Modified header used as a weak validator for resume safety.</summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.DigestAlgorithm">
            <summary>
            Canonical name of the digest algorithm the server advertised (e.g. <c>SHA-256</c>), or null when
            it advertised none. Most servers do not, which is expected and never blocks a download.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.DigestValue">
            <summary>
            The expected content digest as base64, paired with <see cref="P:PDM.Core.Models.RemoteFileInfo.DigestAlgorithm"/>. When present,
            the completed file is hashed and compared against it before being delivered.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.CanSegment">
            <summary>
            True when we know the total size and the server supports ranges, meaning a
            multi-connection segmented download is possible.
            </summary>
        </member>
        <member name="P:PDM.Core.Models.RemoteFileInfo.IsLikelyWebPage">
            <summary>
            True when the URL points at an HTML web page rather than a downloadable file:
            the server returned <c>text/html</c> or <c>application/xhtml+xml</c>. This lets the
            UI warn the user that we'd be downloading a page's HTML source, not the file they
            probably wanted, and suggest the browser extension for capturing real download URLs.
            </summary>
        </member>
        <member name="T:PDM.Core.Net.HttpClientProvider">
             <summary>
             Builds and owns a single <see cref="T:System.Net.Http.HttpClient"/> configured for downloading:
             redirect following, connection pooling suited to many parallel segments,
             automatic decompression disabled (we want raw bytes and accurate sizes), and a
             generous per-request timeout since large transfers are streamed manually.
            
             <para>
             <b>Why WinHTTP instead of the managed SocketsHttpHandler.</b>
             Anti-bot layers such as Cloudflare do not just look at the User-Agent — they fingerprint the
             TLS <c>ClientHello</c> (JA3/JA4: cipher order, extensions, supported groups, ALPN, etc.). .NET's
             managed TLS stack (SslStream, used by SocketsHttpHandler) produces a distinctive fingerprint that
             these services flag as "non-browser bot" and answer with <c>403 Forbidden</c> — for EVERY request,
             regardless of the headers we send. This was reproduced against https://samfw.com: the managed
             handler got 403 on both the file and the site root, on HTTP/1.1 and HTTP/2, on TLS 1.2 and 1.3,
             even with a full set of browser headers, while the OS HTTP stack (winhttp.dll — the same family
             curl.exe, WinINet, and download managers like IDM use) got a normal 206 Partial Content.
             Routing through <see cref="T:System.Net.Http.WinHttpHandler"/> makes PDM present the operating system's TLS
             fingerprint, which these CDNs accept.
             </para>
            
             <para>
             <b>Why we keep an honest, non-browser User-Agent.</b>
             Counter-intuitively, spoofing a Chrome User-Agent over the WinHTTP fingerprint made things WORSE:
             Cloudflare treats "Chrome UA + non-Chrome TLS fingerprint" as an impersonation attempt and blocks
             it (403), whereas an honest client UA ("PerfectDownloadManager/...") over the OS fingerprint is a
             self-consistent, non-deceptive client and is allowed through. So we deliberately do NOT fake a
             browser UA — a consistent identity beats a mismatched disguise.
             </para>
            
             <para>
             On non-Windows platforms (developer machines, CI) we fall back to <see cref="T:System.Net.Http.SocketsHttpHandler"/>
             so the library still builds and runs; the shipping product is Windows-only, where the WinHTTP
             path is always taken.
             </para>
             </summary>
        </member>
        <member name="F:PDM.Core.Net.HttpClientProvider.DefaultUserAgent">
            <summary>Default User-Agent used when the caller does not supply one.</summary>
        </member>
        <member name="F:PDM.Core.Net.HttpClientProvider.MaxConnectionsPerServer">
            <summary>Matches the previous SocketsHttpHandler.MaxConnectionsPerServer so many segments run in parallel.</summary>
        </member>
        <member name="M:PDM.Core.Net.HttpClientProvider.CreateHandler(System.Net.IWebProxy)">
             <summary>
             Creates the platform-appropriate HTTP message handler. On Windows this is the WinHTTP-backed
             handler whose TLS fingerprint CDNs accept (see class remarks); elsewhere it is the managed
             SocketsHttpHandler so the library remains cross-platform for tests and development.
            
             <para>Exposed so the cross-platform <c>PDM.Platform.IHttpHandlerFactory</c> seam can delegate
             to this single source of truth instead of duplicating the handler configuration.</para>
             </summary>
        </member>
        <member name="P:PDM.Core.Net.HttpClientProvider.Client">
            <summary>The shared, configured client. Safe for concurrent use.</summary>
        </member>
        <member name="T:PDM.Core.Net.RemoteFileInspector">
            <summary>
            Probes a URL using a single-byte range request (<c>Range: bytes=0-0</c>). This is
            more reliable than HEAD across real-world servers and tells us in one round trip
            whether ranges are supported (HTTP 206 + Content-Range) and the total size.
            The response body is never read; only headers are consumed.
            </summary>
        </member>
        <member name="M:PDM.Core.Net.RemoteFileInspector.#ctor(System.Net.Http.HttpClient)">
            <summary>Creates an inspector over a preconfigured client (useful for testing/DI).</summary>
        </member>
        <member name="M:PDM.Core.Net.RemoteFileInspector.InspectAsync(System.Uri,System.String,System.Threading.CancellationToken)">
            <inheritdoc />
        </member>
        <member name="T:PDM.Core.Persistence.JsonSettingsStore">
            <summary>
            Loads and persists <see cref="T:PDM.Core.Models.AppSettings"/> as JSON on disk, atomically. Concurrent
            callers see a consistent snapshot; a save always fully replaces the file so partial
            writes cannot corrupt the settings.
            </summary>
        </member>
        <member name="M:PDM.Core.Persistence.JsonSettingsStore.LoadAsync(System.Threading.CancellationToken)">
            <summary>Loads settings from disk, returning defaults when no file exists or when it is invalid.</summary>
        </member>
        <member name="M:PDM.Core.Persistence.JsonSettingsStore.Migrate(PDM.Core.Models.AppSettings)">
             <summary>
             Brings an older settings file up to date. Returns true when something changed and the file
             should be rewritten.
            
             <para>Migrations must only replace values that are recognisably an old <em>default</em>, never a
             value the user may have chosen deliberately — hence the equality checks against the previous
             defaults rather than blanket overwrites.</para>
             </summary>
        </member>
        <member name="M:PDM.Core.Persistence.JsonSettingsStore.SaveAsync(PDM.Core.Models.AppSettings,System.Threading.CancellationToken)">
            <summary>Persists the given settings, replacing any previous file atomically.</summary>
        </member>
        <member name="M:PDM.Core.Persistence.JsonSettingsStore.WriteAsync(PDM.Core.Models.AppSettings,System.Threading.CancellationToken)">
            <summary>
            Writes the settings atomically (temp file then replace). Caller must already hold
            <see cref="F:PDM.Core.Persistence.JsonSettingsStore._gate"/>; kept separate so the migration path in <see cref="M:PDM.Core.Persistence.JsonSettingsStore.LoadAsync(System.Threading.CancellationToken)"/> can reuse it
            without re-entering the non-reentrant semaphore.
            </summary>
        </member>
        <member name="T:PDM.Core.Persistence.JsonSidecarStateStore">
            <summary>
            Stores each <see cref="T:PDM.Core.Models.DownloadState"/> as an individual JSON sidecar file in a
            directory. Writes are atomic (write-to-temp then replace) so a crash during a save
            cannot corrupt the last good state. This is the resume/crash-recovery backbone for
            in-flight downloads; the UI-facing history uses a separate database.
            </summary>
        </member>
        <member name="M:PDM.Core.Persistence.JsonSidecarStateStore.SaveAsync(PDM.Core.Models.DownloadState,System.Threading.CancellationToken)">
            <inheritdoc />
        </member>
        <member name="M:PDM.Core.Persistence.JsonSidecarStateStore.LoadAsync(System.Guid,System.Threading.CancellationToken)">
            <inheritdoc />
        </member>
        <member name="M:PDM.Core.Persistence.JsonSidecarStateStore.DeleteAsync(System.Guid,System.Threading.CancellationToken)">
            <inheritdoc />
        </member>
        <member name="M:PDM.Core.Persistence.JsonSidecarStateStore.LoadAllAsync(System.Threading.CancellationToken)">
            <summary>Enumerates all persisted states, skipping any that fail to deserialize.</summary>
        </member>
        <member name="T:PDM.Core.Serialization.PdmCoreJsonContext">
             <summary>
             System.Text.Json source-generation context for the core serialized models. Using compile-time
             generated metadata (instead of the reflection-based <c>JsonSerializer</c> overloads) is required
             for NativeAOT and trimming: no runtime reflection over these types, no dynamic code generation.
            
             <para>Options mirror the previous hand-configured behaviour: null properties are omitted on write
             and enums serialize as strings. <c>WriteIndented</c> / <c>PropertyNameCaseInsensitive</c> are left
             at their defaults here and applied per-store where they differ (settings are pretty-printed and
             read case-insensitively; sidecar state is compact).</para>
             </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.Boolean">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.AppSettings">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.DownloadCategory">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.DownloadRequest">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.DownloadSegment">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.DownloadState">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.DownloadStatus">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.OverwritePolicy">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.DictionaryDownloadCategoryString">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.ListDownloadSegment">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.DateTimeOffset">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.NullableDateTimeOffset">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.Guid">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.Int32">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.Int64">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.NullableInt64">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.String">
            <summary>
            Defines the source generated JSON serialization contract metadata for a given type.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.Default">
            <summary>
            The default <see cref="T:System.Text.Json.Serialization.JsonSerializerContext"/> associated with a default <see cref="T:System.Text.Json.JsonSerializerOptions"/> instance.
            </summary>
        </member>
        <member name="P:PDM.Core.Serialization.PdmCoreJsonContext.GeneratedSerializerOptions">
            <summary>
            The source-generated options associated with this context.
            </summary>
        </member>
        <member name="M:PDM.Core.Serialization.PdmCoreJsonContext.#ctor">
            <inheritdoc/>
        </member>
        <member name="M:PDM.Core.Serialization.PdmCoreJsonContext.#ctor(System.Text.Json.JsonSerializerOptions)">
            <inheritdoc/>
        </member>
        <member name="M:PDM.Core.Serialization.PdmCoreJsonContext.GetTypeInfo(System.Type)">
            <inheritdoc/>
        </member>
        <member name="T:PDM.Core.Util.AppPaths">
            <summary>
            Resolves the standard on-disk locations PDM uses on Windows: per-user application
            data root, sidecar state directory, history database path, and log directory.
            The layout is derived from <c>%LOCALAPPDATA%\PerfectDownloadManager</c>.
            </summary>
        </member>
        <member name="P:PDM.Core.Util.AppPaths.Root">
            <summary>Local per-user root directory. Created on first use.</summary>
        </member>
        <member name="P:PDM.Core.Util.AppPaths.SettingsFile">
            <summary>Path to the JSON settings file.</summary>
        </member>
        <member name="P:PDM.Core.Util.AppPaths.StateDirectory">
            <summary>Directory holding per-download JSON sidecar state files.</summary>
        </member>
        <member name="P:PDM.Core.Util.AppPaths.DatabaseFile">
            <summary>Path to the SQLite history/catalog database.</summary>
        </member>
        <member name="P:PDM.Core.Util.AppPaths.LicenseFile">
            <summary>Path to the encrypted license record.</summary>
        </member>
        <member name="P:PDM.Core.Util.AppPaths.LogsDirectory">
            <summary>Directory for log files.</summary>
        </member>
        <member name="T:PDM.Core.Util.CategoryClassifier">
            <summary>
            Maps a file name (or URL) to a <see cref="T:PDM.Core.Models.DownloadCategory"/> using its extension.
            The classification is best-effort: unknown extensions fall back to <see cref="F:PDM.Core.Models.DownloadCategory.General"/>.
            </summary>
        </member>
        <member name="M:PDM.Core.Util.CategoryClassifier.Classify(System.String)">
            <summary>Returns the category for a file name or URL, defaulting to General.</summary>
        </member>
        <member name="T:PDM.Core.Util.ContentDigest">
             <summary>
             Reads server-advertised content digests off a response and verifies a downloaded file against
             them. This upgrades PDM's integrity guarantee from "we received the right number of bytes" to
             "we received exactly the right bytes".
            
             <para><b>Entirely opportunistic.</b> Most servers advertise no digest at all. When none is present
             the download proceeds and completes normally — a missing digest is never treated as an error, and
             nothing is slowed down. Verification only runs when the server gave us something to check against.</para>
            
             <para>Supported sources, in the order they are trusted:</para>
             <list type="bullet">
               <item><c>Repr-Digest</c> / <c>Digest</c> (RFC 9530 / RFC 3230), e.g.
                     <c>sha-256=:BASE64:</c> or <c>sha-256=BASE64, md5=BASE64</c>. The strongest offered
                     algorithm wins.</item>
               <item><c>Content-MD5</c> (RFC 1864) as a fallback.</item>
             </list>
             Both base64 (per spec) and hex encodings are accepted, because real-world servers emit both.
             </summary>
        </member>
        <member name="F:PDM.Core.Util.ContentDigest.Md5">
            <summary>Canonical algorithm names used throughout PDM.</summary>
        </member>
        <member name="F:PDM.Core.Util.ContentDigest.Sha1">
            <summary>Canonical algorithm names used throughout PDM.</summary>
        </member>
        <member name="F:PDM.Core.Util.ContentDigest.Sha256">
            <summary>Canonical algorithm names used throughout PDM.</summary>
        </member>
        <member name="F:PDM.Core.Util.ContentDigest.Sha512">
            <summary>Canonical algorithm names used throughout PDM.</summary>
        </member>
        <member name="M:PDM.Core.Util.ContentDigest.TryExtract(System.Net.Http.HttpResponseMessage)">
            <summary>
            Extracts the strongest usable digest advertised by <paramref name="response"/>, or null when the
            server advertised none (by far the common case). The returned value is normalised to base64.
            </summary>
        </member>
        <member name="M:PDM.Core.Util.ContentDigest.ComputeBase64Async(System.String,System.String,System.Int32,System.Threading.CancellationToken)">
            <summary>
            Hashes <paramref name="path"/> with <paramref name="algorithm"/> and returns the digest as
            base64. Streams the file in pooled buffers so hashing a very large download does not allocate
            proportionally to its size, and honours <paramref name="cancellationToken"/> so a pause during
            verification stays responsive.
            </summary>
        </member>
        <member name="M:PDM.Core.Util.ContentDigest.Matches(System.String,System.String,System.String)">
            <summary>
            Compares an expected digest (as advertised by the server, base64 or hex) with a computed base64
            digest. Returns false when either value cannot be decoded, so an unparseable digest is treated
            as a mismatch only by the caller's choice — callers should prefer <see cref="M:PDM.Core.Util.ContentDigest.TryExtract(System.Net.Http.HttpResponseMessage)"/>,
            which already discards undecodable values.
            </summary>
        </member>
        <member name="M:PDM.Core.Util.ContentDigest.IsSupported(System.String)">
            <summary>True when PDM can hash with the named algorithm.</summary>
        </member>
        <member name="M:PDM.Core.Util.ContentDigest.NormalizeAlgorithm(System.String)">
            <summary>Maps the many spellings servers use onto PDM's canonical names.</summary>
        </member>
        <member name="M:PDM.Core.Util.ContentDigest.Strength(System.String)">
            <summary>Preference order: a stronger hash is a better integrity proof.</summary>
        </member>
        <member name="M:PDM.Core.Util.ContentDigest.TryDecode(System.String,System.Int32,System.Byte[]@)">
            <summary>
            Decodes a digest that may be base64 (per the specs) or hex (used by some servers), rejecting
            anything whose decoded length does not match the algorithm.
            </summary>
        </member>
        <member name="T:PDM.Core.Util.FileNameResolver">
            <summary>
            Derives a safe, human-friendly output file name for a download from the server
            response and the request URL. Handles RFC 6266 Content-Disposition (including the
            extended <c>filename*</c> form), percent-encoded URL segments, and sanitization
            of characters that are illegal on Windows file systems.
            </summary>
        </member>
        <member name="M:PDM.Core.Util.FileNameResolver.Resolve(System.Uri,System.Net.Http.Headers.ContentDispositionHeaderValue,System.String)">
            <summary>
            Resolves a file name using, in order of preference: the Content-Disposition
            header, the last path segment of the URL, then a generic fallback.
            </summary>
        </member>
        <member name="M:PDM.Core.Util.FileNameResolver.Sanitize(System.String)">
            <summary>Removes characters that are invalid in Windows file names.</summary>
        </member>
        <member name="T:PDM.Core.Util.PathHelper">
            <summary>Helpers for producing safe, non-colliding output file paths.</summary>
        </member>
        <member name="M:PDM.Core.Util.PathHelper.EnsureUnique(System.String)">
            <summary>
            Returns a path that does not collide with an existing file. If <paramref name="path"/>
            is free it is returned unchanged; otherwise a counter is inserted before the
            extension, e.g. "movie.mp4" -> "movie (1).mp4" -> "movie (2).mp4".
            </summary>
        </member>
        <member name="T:PDM.Core.Util.SparseFile">
             <summary>
             Marks a file as <em>sparse</em> on Windows/NTFS. This is a critical optimization for segmented
             downloads and directly prevents a severe "fast then stalls" failure mode.
            
             <para><b>Why this matters.</b> A parallel download preallocates the output to its final size and
             then writes at many scattered offsets at once (segment 0 at byte 0, segment 15 near the end,
             etc.). On NTFS a file tracks a "valid data length" (VDL); writing beyond the current VDL forces
             the OS to physically zero every byte between the VDL and the new write offset before the write can
             proceed. So the connection that writes near the end of the file makes NTFS zero-fill almost the
             entire file up front. On storage with a throughput cap (typically an Azure/cloud VM disk) that
             burst of zeroing saturates the disk for seconds, back-pressures the network reads (each read waits
             on its write), and idle connections then trip the server/receive timeout — the reported symptom of
             a download that starts fast, collapses to KB/s showing "waiting for server", and eventually fails a
             segment.</para>
            
             <para>Marking the file sparse tells NTFS to skip that zero-fill: unwritten regions are simply not
             allocated and read back as zero with no disk I/O. Once every segment has written, the file is fully
             populated and behaves like any normal file. The logical length is unchanged, so size checks and the
             final rename are unaffected.</para>
            
             <para>Entirely best-effort: on non-Windows platforms, on file systems that do not support sparse
             files, or if the control call fails for any reason, the download proceeds unchanged (just without
             this optimization). It is never allowed to fail a download.</para>
             </summary>
        </member>
        <member name="M:PDM.Core.Util.SparseFile.TryEnable(Microsoft.Win32.SafeHandles.SafeFileHandle)">
            <summary>
            Attempts to mark the file behind <paramref name="handle"/> sparse. No-op off Windows and on any
            failure. Call this on a freshly created part file, before preallocating or writing to it.
            </summary>
        </member>
    </members>
</doc>
