diff --git a/macros/l0.DependencyControl.Toolbox.moon b/macros/l0.DependencyControl.Toolbox.moon index 3c7b2f6..fd43203 100644 --- a/macros/l0.DependencyControl.Toolbox.moon +++ b/macros/l0.DependencyControl.Toolbox.moon @@ -26,6 +26,10 @@ logger.usePrefixWindow = false msgs = { install: { scanning: "Scanning %d available feeds...", + scanningTask: "Scanning available feeds..." + loadingTask: "Loading feed data..." + empty: "All available scripts are already installed." + emptyUnfetched: "No scripts are available to install (%d of %d known feeds could not be fetched)." createScriptUpdateRecordFailed: "Failed to create an update record for %s '%s' from feed %s: %s" } uninstall: { @@ -218,11 +222,16 @@ promptUntrustedFeed = (url, ft) -> -- Crawls the feed inventory with the untrusted-feed prompter active (so the `prompt` policy asks), scoped -- so the prompter never leaks into background fetches. Shared by install discovery and Manage Feeds. -crawlWithPrompt = (inventory) -> +-- The pcall keeps a cancellation raised from onProgress from skipping the prompter reset; +-- returns nil when the crawl was cancelled and re-raises anything else. +crawlWithPrompt = (inventory, onProgress) -> feedTrust = DepCtrl.updater.feedTrust feedTrust\setPrompter promptUntrustedFeed - entries = inventory\crawl! + ok, entries = pcall inventory.crawl, inventory, onProgress feedTrust\setPrompter nil + if not ok and entries != "cancelled" + error entries, 0 + return nil unless ok entries -- Macros @@ -261,8 +270,14 @@ install = -> -- FeedInventory crawls the known feeds, which are trust-gated and bounded. The shared feed loader then -- serves each reachable feed's data from the cache the crawl just populated. macros, modules = {}, {} - entries = crawlWithPrompt buildFeedInventory! - + aegisub.progress.task msgs.install.scanningTask + entries = crawlWithPrompt buildFeedInventory!, (fetched, known) -> + aegisub.progress.set math.floor fetched * 100 / known + error "cancelled", 0 if aegisub.progress.is_cancelled! + aegisub.progress.set 100 + return unless entries + + aegisub.progress.task msgs.install.loadingTask logger\log msgs.install.scanning, #entries for entry in *entries continue unless entry.fetched @@ -273,6 +288,13 @@ install = -> moduleList, moduleMap = buildDlgList modules macroList, macroMap = buildDlgList macros + if #moduleList == 0 and #macroList == 0 + unfetched = #[entry for entry in *entries when not entry.fetched] + message = unfetched > 0 and msgs.install.emptyUnfetched\format(unfetched, #entries) or msgs.install.empty + aegisub.dialog.display {{class: "label", x: 0, y: 0, width: 1, height: 1, label: message}}, + {buttons.close}, {ok: buttons.close, cancel: buttons.close} + return + btn, res = aegisub.dialog.display getScriptListDlg macroList, moduleList return unless btn diff --git a/modules/l0/DependencyControl/FeedInventory.moon b/modules/l0/DependencyControl/FeedInventory.moon index b04ba76..912f141 100644 --- a/modules/l0/DependencyControl/FeedInventory.moon +++ b/modules/l0/DependencyControl/FeedInventory.moon @@ -242,11 +242,12 @@ class FeedInventory ---Fetches feeds and discovers transitively-advertised ones by crawling the `knownFeeds` graph out from the ---config-derived feeds; untrusted expansion is bounded, so check `stats.truncated` for incomplete results. + ---@param onProgress? fun(fetched: integer, known: integer) Called after each feed is fetched, with the running fetch count and the number of feeds currently known to the crawl (more are discovered as it goes, so `known` may grow). ---@return FeedInventoryEntry[] feeds The known feeds, enriched with what the crawl discovered. ---@return FeedCrawlStats stats What the crawl explored and where it stopped short. - crawl: => + crawl: (onProgress) => inventoryEntriesByUrl = @__collectConfigFeeds! - stats = @__crawlKnownFeeds inventoryEntriesByUrl + stats = @__crawlKnownFeeds inventoryEntriesByUrl, onProgress return @__finalize(inventoryEntriesByUrl), stats ---Breadth-first crawl of the `knownFeeds` graph, extending inventoryEntriesByUrl in place with the transitively-discovered @@ -254,8 +255,9 @@ class FeedInventory ---malicious subtree can't starve the others. ---@private ---@param inventoryEntriesByUrl table The config-derived feeds to start from; extended in place. + ---@param onProgress? fun(fetched: integer, known: integer) Called after each feed is fetched (see `crawl`). ---@return FeedCrawlStats stats - __crawlKnownFeeds: (inventoryEntriesByUrl) => + __crawlKnownFeeds: (inventoryEntriesByUrl, onProgress) => c = @config.c limits = c.feeds.crawlLimits or {} defaults = @@defaultCrawlLimits @@ -300,6 +302,7 @@ class FeedInventory continue unless knownFeeds stats.fetched += 1 inventoryEntriesByUrl[feedUrl].fetched = true + onProgress stats.fetched, #queue - head + 1 + stats.fetched if onProgress perFeedUntrusted = 0 perFeedDrops, perRootDrops = newDrops!, newDrops!