Skip to content

feat: complete download acquisition, publisher catalog browsing, and dependency management - #265

Open
undead2146 wants to merge 135 commits into
developmentfrom
feat/ui-downloads
Open

feat: complete download acquisition, publisher catalog browsing, and dependency management#265
undead2146 wants to merge 135 commits into
developmentfrom
feat/ui-downloads

Conversation

@undead2146

@undead2146 undead2146 commented Jan 26, 2026

Copy link
Copy Markdown
Member

Overview

This PR delivers the complete Downloads & Content Acquisition Experience along with robust Publisher Catalog Browsing, Multi-Axis Variant Management, and Profile Content/Dependency Integration for GenHub.

It establishes a modern, responsive UI backed by resilient download coordination, multi-stage progress tracking, image caching with memory & disk bounds, topological dependency resolution, and seamless profile linking via CAS (Content Addressable Storage).


Key Features & Changes

1. 🌐 Downloads Browser & Catalog Navigation

  • Unified Downloads View (DownloadsBrowserView / DownloadsBrowserViewModel):
    • Replaces legacy publisher card views with a searchable, filterable grid and detail browser.
    • Multi-publisher support with dynamic switching between official repositories, third-party creators, and community catalogs (ModDB, CNC-Labs, AOD Maps, Community Outpost, SuperHackers, GitHub Releases).
    • Real-time search, sorting, category filtering, and game type filtering.
  • Publisher Sidebar & Confirmation Dialog (PublisherSidebarView, SubscriptionConfirmationDialog):
    • Subscription management allowing users to browse, subscribe, and refresh external publisher catalogs with trust boundaries.

2. 📦 Content Detail, Variants & Bundle Components

  • Rich Content Detail (ContentDetailView / ContentDetailViewModel):
    • Comprehensive metadata rendering: title, author, version, description (with HTML stripping & formatting), screenshots carousel, release notes, license, and download sizes.
  • Multi-Axis Variant & Bundle Management (VariantSelectionView, VariantAxisGrouping, VariantSwap):
    • Supports complex multi-axis content variants (e.g. resolution, language, engine version).
    • Bundle component resolution allowing composite packages and modular add-on selection.

3. 🔗 Dependency Resolution & Preview

  • Dependency Graph Analysis (DependencyResolver, DependencyPreviewView):
    • Automatically resolves direct and transitive content dependencies.
    • Interactive preview displaying missing, satisfied, or conflicting dependencies before installation.
    • Profile-aware checks to prevent circular dependencies or incompatible versions.

4. 🎮 Profile Integration & Content State Tracking

  • Profile Selection & Content Assignment (ProfileSelectionView, ProfileSelectionViewModel):
    • One-click installation to existing profiles or immediate creation of new tailored profiles.
    • Dynamic state tracking (ContentStateService) reflecting installation status (NotInstalled, Installing, Installed, UpdateAvailable, Broken) across all profiles.
  • Profile Content Management (ProfileContentService, ProfileEditorFacade):
    • Integrates with CAS storage (CasPoolManager, CasService, ProfileContentLinkerService) for atomic hardlinking/symlinking into workspace directories.

5. ⚡ Infrastructure, Performance & Image Caching

  • Asynchronous Image Loading (ImageCacheService, ImageLoader):
    • Resilient, disk- and memory-bounded caching for thumbnails, screenshots, and publisher logos.
    • Asynchronous background decoding preventing UI thread stalls.
  • Value Converters & UI Helpers (Converters/):
    • Specialized Converters for state-to-brush, state-to-icon, comparison operations, byte formatting, and HTML stripping.
  • Dependency Injection & Module Composition:
    • Clean registration across DownloadModule, ContentPipelineModule, SharedViewModelModule, and ManifestModule.

6. 🧪 Testing & Documentation

  • 75+ Unit & Integration Test Suites (GenHub.Tests):
    • Thorough test coverage across catalog parsers, discoverers, download coordinator, profile content service, image caches, and converters.
  • Comprehensive Documentation (docs/):
    • Architecture flowcharts (docs/FlowCharts/Downloads-Flow.md).
    • Feature specifications (docs/features/downloads.md, docs/features/content/).
    • Developer guides for manifest IDs, catalog identity remediation, and acceptance criteria.

Verification

  • Compile & Build Safety Check: Verified with scripts/build-check.ps1 (0 Warnings, 0 Errors).
  • Review Threads: All 547 PR review threads verified and resolved.

Note

Add publisher catalog browsing, variant-aware download acquisition, and dependency management

  • Introduces a full downloads browser (DownloadsBrowserViewModel, ContentDetailViewModel, ContentGridItemViewModel) replacing the previous DownloadsViewModel, with per-publisher filter panels, variant axis selection, and bundle component support.
  • Adds a generic publisher catalog pipeline: JsonPublisherCatalogParser, GenericCatalogDiscoverer, GenericCatalogResolver, GenericCatalogManifestFactory, PublisherSubscriptionStore, and PublisherCatalogRefreshService enabling third-party catalogs to be fetched, validated, and browsed.
  • Rewrites ContentOrchestrator acquisition pipeline with staged, non-regressing progress reporting, optional IContentDeliverer fallback, publisher factory post-processing for multi-variant manifests, and provenance fields (OriginalProviderName/OriginalContentId).
  • Upgrades ModDBDiscoverer to use Playwright for Cloudflare-protected scraping with RSS fallback, challenge detection, richer item parsing, and multi-filter support.
  • Adds ProfileContentService multi-item add/create overloads with pairwise conflict validation, dependency acquisition, and publisher-specific GameClient synthesis.
  • Registers genhub:// URI scheme on Windows startup and handles catalog subscription deep-links via drag-and-drop onto the main window or IPC forwarding from secondary instances.
  • Adds ArchivePayloadProcessor, ControlBarPackageProcessor, and PlaywrightService (with managed Chromium provisioning) as shared infrastructure services.
  • Risk: many content pipeline services change from scoped/transient to singleton lifetime; state accumulated during one operation may persist across subsequent operations in the same process session.

Macroscope summarized a53b821.

Greptile Summary

Implements an end-to-end content acquisition and profile integration experience.

  • Adds publisher catalog browsing, subscriptions, filtering, content details, and variant selection.
  • Introduces download coordination, archive processing, manifest generation, dependency resolution, and CAS-backed profile linking.
  • Adds bounded image caching, Playwright-based discovery, dependency-injection composition, UI views, documentation, and extensive tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
GenHub/GenHub/Features/Content/Services/Catalog/PublisherSubscriptionStore.cs Adds serialized, lock-protected subscription persistence with detached cache values and atomic file replacement.
GenHub/GenHub/Features/Content/Services/Catalog/GenericCatalogDiscoverer.cs Adds generic publisher discovery, release hydration, stable catalog identities, and cancellation-aware external metadata retrieval.
GenHub/GenHub/Features/Downloads/Services/ContentDownloadCoordinator.cs Coordinates multi-stage content acquisition and reports progress through the downloads workflow.
GenHub/GenHub/Features/GameProfiles/Services/ProfileContentService.cs Integrates acquired manifests and stored content with selected game profiles.
GenHub/GenHub.Core/Models/Manifest/ManifestVariantResolver.cs Adds deterministic variant and executable-entry-point resolution for generated manifests.
GenHub/GenHub/Infrastructure/DependencyInjection/ContentPipelineModule.cs Registers the expanded catalog, content pipeline, publisher, parser, and processing services.
GenHub/GenHub/Features/Downloads/ViewModels/DownloadsBrowserViewModel.cs Implements publisher switching, discovery, filtering, selection, and installation orchestration for the unified downloads browser.

Sequence Diagram

sequenceDiagram
    actor User
    participant Browser as Downloads Browser
    participant Catalog as Publisher Catalog
    participant Pipeline as Acquisition Pipeline
    participant Manifest as Manifest System
    participant CAS as CAS Storage
    participant Profile as Game Profile
    User->>Browser: Browse and select content
    Browser->>Catalog: Discover releases and variants
    Catalog-->>Browser: Content metadata and dependencies
    User->>Browser: Install selected variant
    Browser->>Pipeline: Acquire content
    Pipeline->>Pipeline: Download, validate, and process archive
    Pipeline->>Manifest: Generate content manifest
    Manifest->>CAS: Store content files
    CAS-->>Profile: Link content into profile workspace
    Profile-->>User: Content available for launch
Loading

Reviews (55): Last reviewed commit: "feat(downloads): complete download acqui..." | Re-trigger Greptile

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

Comment thread GenHub/GenHub.Core/GenHub.Core.csproj Outdated
@kilo-code-bot

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

coderabbitai Bot added a commit that referenced this pull request Jan 26, 2026
Docstrings generation was requested by @undead2146.

* #265 (comment)

The following files were modified:

* `GenHub/GenHub.Core/Interfaces/Providers/IPublisherCatalogParser.cs`
* `GenHub/GenHub.Core/Interfaces/Providers/IPublisherCatalogRefreshService.cs`
* `GenHub/GenHub.Core/Interfaces/Providers/IPublisherSubscriptionStore.cs`
* `GenHub/GenHub.Core/Interfaces/Providers/IVersionSelector.cs`
* `GenHub/GenHub.Core/Models/Manifest/ManifestIdGenerator.cs`
* `GenHub/GenHub/App.axaml.cs`
* `GenHub/GenHub/Common/ViewModels/MainViewModel.cs`
* `GenHub/GenHub/Common/Views/MainWindow.axaml.cs`
* `GenHub/GenHub/Features/Content/Services/Catalog/GenericCatalogDiscoverer.cs`
* `GenHub/GenHub/Features/Content/Services/Catalog/GenericCatalogResolver.cs`
* `GenHub/GenHub/Features/Content/Services/Catalog/JsonPublisherCatalogParser.cs`
* `GenHub/GenHub/Features/Content/Services/Catalog/PublisherCatalogRefreshService.cs`
* `GenHub/GenHub/Features/Content/Services/Catalog/PublisherSubscriptionStore.cs`
* `GenHub/GenHub/Features/Content/Services/Catalog/VersionSelector.cs`
* `GenHub/GenHub/Features/Content/Services/CommunityOutpost/GenPatcherDatCatalogParser.cs`
* `GenHub/GenHub/Features/Content/Services/ContentCacheService.cs`
* `GenHub/GenHub/Features/Content/Services/ContentDiscoverers/AODMapsDiscoverer.cs`
* `GenHub/GenHub/Features/Content/Services/ContentDiscoverers/FileSystemDiscoverer.cs`
* `GenHub/GenHub/Features/Content/Services/ContentDiscoverers/ModDBDiscoverer.cs`
* `GenHub/GenHub/Features/Content/Services/ContentOrchestrator.cs`
* `GenHub/GenHub/Features/Content/Services/ContentResolvers/AODMapsResolver.cs`
* `GenHub/GenHub/Features/Content/Services/ContentResolvers/CNCLabsMapResolver.cs`
* `GenHub/GenHub/Features/Content/Services/ContentResolvers/ModDBResolver.cs`
* `GenHub/GenHub/Features/Content/Services/GeneralsOnline/GeneralsOnlineDiscoverer.cs`
* `GenHub/GenHub/Features/Content/Services/GeneralsOnline/GeneralsOnlineJsonCatalogParser.cs`
* `GenHub/GenHub/Features/Content/Services/GitHub/GitHubReleasesDiscoverer.cs`
* `GenHub/GenHub/Features/Content/Services/IContentCacheService.cs`
* `GenHub/GenHub/Features/Content/Services/Parsers/ModDBPageParser.cs`
* `GenHub/GenHub/Features/Content/Services/Publishers/ModDBManifestFactory.cs`
* `GenHub/GenHub/Features/Content/Services/Tools/PlaywrightService.cs`
* `GenHub/GenHub/Features/Content/ViewModels/Catalog/SubscriptionConfirmationViewModel.cs`
* `GenHub/GenHub/Features/Downloads/Services/ContentStateService.cs`
* `GenHub/GenHub/Features/Downloads/Services/IContentStateService.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/ContentDetailViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/ContentGridItemViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/DownloadsBrowserViewModel.InstallationCheck.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/DownloadsBrowserViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/DownloadsViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/AODMapsFilterViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/CNCLabsFilterViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/CommunityOutpostFilterViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/FilterPanelViewModelBase.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/GitHubFilterViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/IFilterPanelViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/ModDBFilterViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/StaticPublisherFilterViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/Filters/SuperHackersFilterViewModel.cs`
* `GenHub/GenHub/Features/Downloads/ViewModels/ProfileSelectionViewModel.cs`
* `GenHub/GenHub/Features/Downloads/Views/ContentCardView.axaml.cs`
* `GenHub/GenHub/Features/Downloads/Views/ContentDetailView.axaml.cs`
* `GenHub/GenHub/Features/Downloads/Views/DownloadsBrowserView.axaml.cs`
* `GenHub/GenHub/Features/Downloads/Views/FilterPanelView.axaml.cs`
* `GenHub/GenHub/Features/Downloads/Views/ProfileSelectionView.axaml.cs`
* `GenHub/GenHub/Features/Downloads/Views/PublisherSidebarView.axaml.cs`
* `GenHub/GenHub/Features/Downloads/Views/SubscriptionConfirmationDialog.axaml.cs`
* `GenHub/GenHub/Features/Settings/ViewModels/SettingsViewModel.cs`
* `GenHub/GenHub/Features/Settings/Views/SettingsView.axaml.cs`
* `GenHub/GenHub/Infrastructure/Converters/ComparisonConverters.cs`
* `GenHub/GenHub/Infrastructure/Converters/NotEqualToConverter.cs`
* `GenHub/GenHub/Infrastructure/DependencyInjection/ContentPipelineModule.cs`
* `GenHub/GenHub/Infrastructure/DependencyInjection/SharedViewModelModule.cs`
greptile-apps[bot]

This comment was marked as resolved.

@greptile-apps

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Comment thread GenHub/GenHub/Features/Content/Services/Catalog/GenericCatalogResolver.cs Outdated
greptile-apps[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

@greptile-apps

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

undead2146 and others added 19 commits August 22, 2026 09:27
…edGroupsAsync for zero-delay async streaming
Path.Combine(InstallationPath, "gensec.big").FileExistsCaseInsensitive() ||
Path.Combine(InstallationPath, GameClientConstants.SuperHackersGeneralsExecutable).FileExistsCaseInsensitive();

if (!isZhNamed || isStrictGeneralsOnlySignature)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Generals detection is suppressed for Zero-Hour-named directories

The condition !isZhNamed || isStrictGeneralsOnlySignature prevents Generals from being detected in directories whose name contains "Zero Hour" unless they also contain gensec.big or SuperHackersGeneralsExecutable. If a directory named "Zero Hour" contains both generals.exe and INI.big, the old code detected both games; the new code detects only Zero Hour. This behavioral regression affects dual-game directories and should be re-evaluated against the intended detection contract.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

}
}
}
catch (IOException)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: .big file scan has incomplete exception handling

The try block around DirectoryInfo.GetFiles("*.big") catches only IOException and UnauthorizedAccessException, but GetFiles can also throw SecurityException, PathTooLongException, and ArgumentException on certain platforms or filesystem configurations. Unhandled exceptions here would crash installation detection instead of falling back gracefully.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


// Act 2: Complete Publisher A's background fetch and allow background task to commit to cache
tcsA.SetResult(OperationResult<ContentDiscoveryResult>.CreateSuccess(new ContentDiscoveryResult { Items = itemsA, TotalItems = 3 }));
await Task.Delay(250);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Fixed 250ms delay replaces robust polling for cache commit

The old implementation polled viewModel.IsLoading with a 5-second timeout, guaranteeing the background task had time to commit to cache before assertions. The new fixed Task.Delay(250) provides no such guarantee and may cause intermittent test failures under CI load or on slower machines where cache commit exceeds 250ms.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

if (Directory.Exists(path))
{
var directoryInfo = new DirectoryInfo(path);
if (directoryInfo.EnumerateFiles().Any(f => f.Name.EndsWith(GameClientConstants.ZeroHourArchiveExtensionSuffix, StringComparison.OrdinalIgnoreCase)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: EnumerateFiles() accidentally dropped the "*.big" filter from the Zero Hour archive scan

The previous code used GetFiles("*.big") to restrict enumeration to .big archives before checking for the ZH.big suffix. The new EnumerateFiles() scans every file in the directory, which changes behavior for files like SomethingZH.big.txt (now incorrectly matched), adds unnecessary I/O for large directories, and defeats the intent of scanning only big archives. Restore the filter: EnumerateFiles("*.big").


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@sonarqubecloud

Copy link
Copy Markdown

@kilo-code-bot kilo-code-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incremental review of 84bf3bf (since 4ed5b74): reviewed the GameInstallation primary-constructor refactor and the GameInstallationService null-guard change.

/// <param name="installationPath">The installation path.</param>
/// <param name="installationType">The installation type.</param>
/// <param name="logger">Optional logger instance.</param>
public class GameInstallation(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Constructor creation log silently dropped in refactor

The removed constructor logged Created GameInstallation: Path={InstallationPath}, Type={InstallationType} at Debug level on every instantiation; the primary-constructor replacement has no equivalent. If creation-time observability still matters, emit it from an initializer or the first Fetch()/SetPaths() call - or confirm the drop was intentional, since the refactor is otherwise behavior-preserving.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


foreach (var group in manifestsByPath)
{
var sourcePath = group.Key;
if (string.IsNullOrEmpty(sourcePath))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Null guard is unreachable and skips silently

The Where above already filters out manifests with null/empty SourcePath (logging each skip at Debug level), so group.Key can never be null or empty here and this continue is dead code. It works as null-safety for the removed !, but the silent skip is inconsistent with the logged filter - consider logging a warning if it ever fires, or restructuring so the double check is not needed, so a future edit to the filter cannot silently discard groups.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant