Brazen Framework - Download Manager

Cross-tab download queue, resolution pipeline, and immediate downloads for Brazen user scripts

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/587126/1884339/Brazen%20Framework%20-%20Download%20Manager.js

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

Author
brazenvoid
Version
2.1.0
Created
2026-07-15
Updated
2026-07-24
Size
246 KB
License
GPL-3.0-only

Brazen Framework — Download Manager (developer guide)

Cross-tab batch downloads, page resolution, immediate GM_download orchestration, optional tag discovery review, and human-interaction rate-limit handling. Apps extend BrazenFramework, call configureDownloadManager() after configureDock(), and @require this module after Framework core. Selection tiles and progress panels are native HTMLElement.

Greasy Fork: Download Manager · Requires: Framework core, IndexedDB Storage (v1.1.0+ download queue stores) · Grant on this module: GM_download


When to use / when not to use

  • Use when your app needs cross-tab download queues, search-tile selection enqueue, media-page immediate download, token-based paths, duplicate-ledger integration, or tag-discovery review before filenames are built.
  • Do not use for a single fire-and-forget GM_download with no queue, no cross-tab state, and no path tokens — that is rare; most Brazen apps still benefit from ledger + path helpers here.
  • Requires a dockconfigureDownloadManager() throws if configureDock() was not called first.
  • Requires IndexedDB — queue rows and shared state live in IDB via Configuration Manager repositories; when IDB is blocked, persistence gates apply.

Quick start

Load order (after Framework core):

// @require … Brazen Framework - Framework.js
// @require … Brazen Framework - Download Manager.js

In the app constructor (after configureDock):

this.configureDock({ orientations: ['right'], scriptName: 'My App', showBranding: true })

this.configureDownloadManager({
  enableConfigKey: 'enable-download-manager',
  downloadPaths: {
    folderConfigKey: 'download-folder',
    filenamePatternConfigKey: 'filename-pattern',
    subfolderPatternConfigKey: 'subfolder-pattern',
    defaultFolder: 'my-downloads',
    getPatternResolver: () => ({
      chips, tagTypes, ignore, substitutions, unknownDefault,
    }),
    extractMediaData: (el) => ({ id, md5, ext }),
    extractTagGroups: () => ({ author: [], character: [], general: [] }),
    extractTagIncidences: () => ({ 'tag name': 12 }), // optional; discovery panel counts
    appendExtension: true,
    nameFallback: (data) => data.id || 'media',
  },
  pages: {
    search: {
      roles: ['selection'],
      itemSelector: 'span.thumb',
      resolveItem: (item) => ({ itemId, sourceUrl }),
      setItemProgress: (item, progress) => { /* optional tile overlay */ },
    },
    media: {
      roles: ['enqueueMedia', 'immediateDownload'],
      defaultDownloadType: 'post',
    },
  },
  downloadTypes: {
    post: {
      resolveFromSearch: (ctx) => ({ nextUrl: ctx.sourceUrl }),
      resolveFromMedia: (doc, ctx) => ({ mediaUrl, tagGroups, downloadId, data }),
    },
  },
  getQueueItemId: (ctx) => ctx.itemId,
  resolutionInitiationGapMs: 2000,
  downloadInitiationGapMs: 2000,
  linkQueues: false, // default; set true when site + CDN share rate limits
})

BrazenDownloadManager.initialize() runs from Framework init() after Configuration Manager setup.


Configuration shape

Top-level keys

Key Role
enableConfigKey Flag field key for Enable Download Manager (auto-registered unless enabled: true)
enableDefault Default for enable flag when auto-registered
enabled When true, skip enable-flag registration and treat manager as always on
linkQueues When true, InterleavedPolicy: one serial dispatcher, any-lane HI pauses the shared regime, download may run while tag discovery gates resolution. Default false (IndependentPolicy): concurrent lanes; tag discovery pauses resolution only; each lane's human-interaction block stops only that lane (both panels may show). Use true when site and download CDN share rate limits.
selectionModeDefaultConfigKey Optional override for Start in Selection Mode (OPTION_DOWNLOAD_SELECTION_MODE_DEFAULT)
downloadPaths Path/token extraction and pattern resolver (see below)
pages Named page configs with roles (see below)
downloadTypes Per-type resolution handlers (resolveFromSearch, resolveFromMedia, …)
getQueueItemId (context) => string stable queue id
resolutionInitiationGapMs ms between resolution fetches (default 2000)
downloadInitiationGapMs ms between GM_download calls (default 2000; replaces Framework downloadsDelay)
tagDiscovery Optional review panel config (see below)
rateLimitHandlers Per-context rate-limit strategies (see below)
queueDockSlideOut Optional Start/Pause slide-out overrides: childFields (default dock-download-leader, dock-clear-download-queue), getSlideOutNodes (default progress slot), slideOutWhen / slideOutPinnedWhen

Auto-registered Behaviours flags

_registerConfigFields always registers (when missing):

Constant Key Title
OPTION_DOWNLOAD_SELECTION_MODE_DEFAULT download-selection-mode-default Start in Selection Mode (place with createElement on Downloads)
OPTION_REVIEW_IGNORED_FILENAME_PINS review-ignored-filename-pins Review Ignored Download Path Tags
OPTION_SKIP_EMPTY_FILENAME_PINS skip-empty-filename-pins Skip Media Without Download Path Tags
OPTION_ONLY_SHOW_DOWNLOAD_MANAGER only-show-download-manager Only Show the Download Manager

Mount the last three via Framework createBehavioursTabPanel(). Only Show the Download Manager hides dock buttons marked immediateDownload (autoDownload, removeMediaOnDownload, singleDownload templates) via Configuration Manager include evaluation.

downloadPaths

Key Role
folderConfigKey / filenamePatternConfigKey / subfolderPatternConfigKey Configuration Manager keys for folder and patterns
defaultFolder Fallback root folder
getPatternResolver () => { chips, tagTypes, ignore, substitutions, unknownDefault, … } — tag-type tokens use registry attributes when TagRuntime is warmed
extractMediaData(el) Metadata object for pattern chips (id, md5, ext, …)
extractTagGroups() { typeKey: string[] } tag lists for path tokens
extractTagIncidences() Optional { tagName: count } map for discovery panel incidence meta
appendExtension Append data.ext to filename when set
nameFallback(data) When pattern resolves empty

Path helpers (buildDownloadPathFromPatterns, buildDownloadPath, sanitizePathSegment, substitution parse/map) live on the manager instance — use getDownloadManager() from the app.

pages and roles

Each pages[pageName] entry:

Key Role
roles Capability flags for the active page (see table)
itemSelector Search tiles for selection mode
resolveItem(item) (item: HTMLElement) => { itemId, sourceUrl, … } for enqueue
defaultDownloadType Fallback type key for media enqueue
setItemProgress(item, progress) Optional hook for per-tile pipeline UI (item: HTMLElement)
Role Enables
selection Selection mode + click-to-enqueue on search tiles
enqueueMedia Media-page add/remove queue toggle
immediateDownload downloadImmediate() on media pages
tagDiscoveryToggle Dock tag-discovery mode button
dashboard Dedicated control / future-analytics host. Does not grant selection, enqueue, immediate download, or tag-discovery toggle by itself. Does not auto-claim processor leadership — crown / sole-tab rules unchanged. Apps decide which dock buttons to hide on their dashboard page. Framework exposes isDashboardPage() for this role.

Query with isDownloadPageRole('selection') (delegated on Framework).

tagDiscovery (optional)

Key Role
discoverAt Steps that scan pattern-relevant types, e.g. ['resolution', 'immediate']
tagTypes Panel grouping: { label, rowClass, color } per type — colors via View Layer applyTagDiscoveryTypeColors
panel Extra options forwarded to renderTagDiscoveryPanelContent (section labels, groupOrder, …)
actions Incidence options for tag-attribute buttons (buildUrl, field keys, CSS, ensureOptionKey) — framework owns button chrome
isTagKnown(tagName) Optional override; default is TagEntry.isDiscovered === true
ignoredPinReview Optional { isEnabled() } — reopen when any blocking tag-type pin in the active filename/subfolder patterns is fully filename-ignored (post had tags for that type; join ends empty; at least one ignored tag). Lists only those blocking types. When omitted, uses OPTION_REVIEW_IGNORED_FILENAME_PINS.
skipEmptyFilenamePins Optional { isEnabled() } — queue-only drop before promote when active patterns have tag-type pins and the post has no raw tags for any of them (ignore list not applied; no-op with no tag pins). When omitted, uses OPTION_SKIP_EMPTY_FILENAME_PINS.

When discovery is on, undiscovered tags (isDiscovered null) set resolutionBlocked and open #bv-tag-discovery-panel on the focused tab (tagDiscoveryPanelTabId). With default linkQueues: false, that pauses resolution only (downloads continue). With linkQueues: true, both pipelines pause. Confirm marks tags discovered and promotes types when unset (any tab that owns the panel); attribute actions may record type without clearing discovery. Skip drops the item without marking discovered; Open media opens the source URL; deselect/dequeueDownload of the blocked item also clears the gate (like Skip). Opening review (_presentTagDiscoveryReview) and Confirm / Skip wake other tabs via _signalProcessorsWake() so a focused follower can show the panel when the processor leader is in the background, and an idle leader can resume after Confirm / Skip on a follower. Hiding or unloading a tab releases panel ownership (not the queue gate) so another search tab can restore the panel; Confirm / Skip / deselect of the blocked item clear resolutionBlocked. State fields discoveryPanelTags, discoveryPanelKnownTags, and discoveryReviewMode (unknown | ignoredPins) persist in downloadManagerState. After New/Known render, the manager prefetches a Similar tags list (up to 10 same-type registry matches; bounded during scan; in-flight work cancelled on hide while a completed cache is kept for restore; reused across debounced attribute refreshes without collapsing the accordion). Clicking a tag name opens actions.bookmark.buildUrl in a new tab. When ignoredPinReview is enabled, the queue and immediate paths open the same panel if any blocking pin type would be empty after ignore — after unknown Confirm, or instead of promoting when there are no unknowns. Panel content is only ignored tags for those empty pin types (not healthy siblings); Confirm continues only when no blocking pin remains. Ignored-pin mode starts Similar expanded with no count in the heading (similarOpen / similarShowCount via View Layer). When skipEmptyFilenamePins is enabled, the queue path drops the resolution item (no download promote) after those gates if no pinned tag type has raw tags on the post. Immediate/auto download is not gated by empty-pin skip.

rateLimitHandlers

Keyed by context (resolution, download, …). Each context may define:

Handler Role
humanInteraction { detect(ctx), openUrl?(ctx), confirmTitle?, confirmMessage?, confirmLabel?, reopenLabel?, softExpireMs? } — sets that context's humanInteraction[context] lane entry (including openUrl), shows the per-lane panel on a visible tab (Open Cloudflare Challenge then Done — resume). Background processor tabs leave promptTabId unset and wake peers so the focused tab can steal. Does not auto-open the challenge tab; Open reads the lane openUrl from the sync cache and opens in the click stack (brazen_hi=1 + brazen_hi_ctx query/hash, rel=opener). Optional softExpireMs (default 120000) — after that age the lane is cleared (next resolve re-blocks if CF remains). Verification tabs are marker-only. Runs before timedReload.
timedReload { detect(doc, ctx), reloadDelayMs? } — document detect only (not bare HTTP 429)

reopenLabel defaults to Open Cloudflare Challenge on the human-interaction panel. Each lane stores its own openUrl for the Open action.

Initiation gaps

resolutionInitiationGapMs and downloadInitiationGapMs pace resolution fetches and GM_download respectively (defaults 2000 each; configure independently per app). Both use separate in-tab clocks hydrated from lastResolutionInitiationAt / lastDownloadInitiationAt in shared state so concurrent IDB writes cannot erase the gap. Resolution paces before every fetch attempt (including rate-limit retries). Confirming a human-interaction rate limit stamps only the blocked pipeline's clock so the next initiation waits a full gap without affecting the other queue.


Public API

Framework delegates most calls; you can also use this.getDownloadManager() for path helpers.

Queue and download

Method Role
downloadImmediate({ mediaElement, removeMediaOnSuccess?, source? }) Immediate download; Tag Discovery when unknowns; never batch queues
enqueueDownload(context) Add to resolution queue; returns whether added
dequeueDownload(itemId) Remove from either queue; cancels Tag Discovery when removing the blocked review item; clears any HI lane whose itemId matches
isQueued(itemId) Cross-tab pipeline membership
toggleDownloadManagerPaused() Start/pause download queue only; no-op when download pending is 0
paceResolutionInitiation() Wait resolutionInitiationGapMs (serialized) before the next same-origin HTML fetch
clearDownloadQueue() Wipe download queue only; clears download-context human-interaction block
toggleCurrentMediaQueued() Media-page enqueue toggle
toggleSelectionMode() Local session selection mode (search)

Tag discovery

Method Role
confirmTagDiscoveryMappings() Mark tags discovered (isDiscovered); promote types when unset; promote resolution item or run pending immediate
skipTagDiscoveryInclusion() Skip review — drop item and clear selection progress; do not confirm types
openTagDiscoveryMedia() Open media URL for item under review
toggleTagDiscoveryMode() Cross-tab discovery enable toggle
restoreTagDiscoveryPanelIfNeeded() Re-open the review panel when resolutionBlocked (after dock build / on tab focus)

Progress and status

Method Role
getDownloadManagerProgress() { resolution: {current, total}, download: {current, total} } — both always live (dock panel shows while either queue has pending work)
getDownloadQueueCount() Dock counter; excludes resolution queue when discovery mode is on
getPendingPipelineCount() Non-terminal items in both queues
isDownloadManagerEnabled() Enable flag + dock active
isDownloadPageRole(role) Page role gate
isDownloadManagerLeaderTab() This tab owns processingTabId
isDownloadManagerRunning() Pipeline active (queues, blocked state, or in-tab processing)
requestDownloadManagerLeadership() Take processor leadership when idle; alert and abort while the download manager is active

Human interaction

Method Role
BrazenDownloadManager.peekHumanInteractionBlockedMirror(scriptPrefix) Static. Sync localStorage mirror — usable in Phase-1 page ops before initialize()
BrazenDownloadManager.peekHumanInteractionBlockedIdb(scriptPrefix) Static. Async IndexedDB read — true when any HI lane is blocked (Phase-1 fallback when query/hash/mirror/opener were stripped)
BrazenDownloadManager.hasBrazenHumanInteractionMarker() Static. Query or hash still has brazen_hi
BrazenDownloadManager.isQueueOpenedVerificationTab() Static. Same-origin opener (queue-opened tab)
BrazenDownloadManager.shouldSilenceMediaCloudflarePrompt(scriptPrefix, instance?) Static. Whether a challenge page should stay silent
handleMediaCloudflarePage(options?) Phase-1 Cloudflare / challenge page: queue verification tabs show Done — resume / Done — resume — close tab; standalone shows themed Done — reload pane
shouldSilenceMediaCloudflarePrompt() Instance wrapper around the static silence gate
isHumanInteractionBlockedSync() Instance: true when any HI lane is blocked

Path and substitution helpers (getDownloadManager())

Method Role
buildDownloadPathFromPatterns(data, tagGroups) Token path assembly
buildDownloadPath(folder, name) Sanitized folder + filename
sanitizePathSegment(segment) Illegal char strip + entity decode
decodeHtmlEntities(text) Delegates to Utilities.decodeHtmlEntities
parseDownloadTagSubstitutionLines(lines, normalizeToken) Substitution parser ( / -> / -; also object rows)
buildDownloadTagSubstitutionMap(rules) Map<subject, replacement>

Processors and leadership

One leader tab (processingTabId) owns both pipelines via _runDispatcher. With default linkQueues: false (IndependentPolicy), resolution and download run as concurrent lane loops. With linkQueues: true (InterleavedPolicy), one serial dispatcher prefers resolution then download.

Behaviour Detail
paused Pauses the download queue only. Resolution auto-runs on enqueue. Once started, idle (auto-pause / Start disabled / download counter reset) only when both queues are empty.
resolutionBlocked Tag discovery gate. Independent: stops resolution only (downloads continue). Interleaved: gates resolution only — download may run opportunistically while the user reviews tags.
humanInteraction Per-lane rate-limit gate. Independent: each lane's entry stops only that lane (both panels may show). Interleaved: any lane HI pauses the shared regime.
Download interruption Before GM_download, the row sets inProgress / startedAt. On a visible full-UI steal, orphan downloading+inProgress rows are deferred (not requeued) and #bv-download-interruption-panel shows a 5s timed button (hover pauses). Click → mark done + paused; timeout → requeue + relaunch. Download lane gated while pending.
Leadership Heartbeat processingHeartbeatAt (~3 s) + sync localStorage mirror. Stale steal ~12 s or immediate after pagehide mirror clear. Steal requeues in-flight resolving / downloading rows (never requeues this tab's active item; defers inProgress orphans when the page can show UI). Unload hard-interrupts local processors and closes IndexedDB; bfcache restore reopens storage and requeues orphans before renewing the lock. Dock crown (requestDownloadManagerLeadership) force-claims onto the current tab only when isDownloadManagerRunning() is false; active crown cannot resign.
Claims Items move queuedresolving / downloading before work; in-tab active-item guards abort terminal puts after steal or clear. Terminal rows are pruned after each task (dock totals use completed counters). Interleaved mode can retract an in-flight job back to queued when pause / any-HI lands mid-process.
Ledger Duplicate claim at download time in Download Manager when Skip or Hide is on; ledgerClaimed on download rows prevents double-claim after lock steal.
UI cadence Queue/state puts do not rebuild dock; one UI reset per resolution/download task. Full _syncFromStorage on init, enqueue, Start, and when a visible tab wakes; hidden tabs only resume leadership/processors. Wake signal + receiver are debounced (~150 ms). Pending counts use indexed IDB counts (not full-queue loads).

Tag discovery UI

Scans pattern-relevant tag types only (optional tagDiscovery.tagTypes array is an allowlist filter, not a full-type override). Always registers path tags via _registerResolvedTagGroups (discovery on → seen-only; off → typeEntryId + isDiscovered). Panel lists undiscovered then discovered tags with incidence counts; framework maps tag-attribute actions internally (type may be set without discovering). Reload / tab focus restores panel when review pending. Promote / Confirm wake the download processor so Ready rows are not stranded.

Human-interaction panels

Per-lane dock panels (#bv-human-interaction-panel-resolution / #bv-human-interaction-panel-download) on the focused tab when that lane is blocked (any search/media tab may steal that lane's promptTabId; hiding/unloading releases ownership only — the block stays until Done, soft-expiry TTL clear, or dequeue of the HI subject). Background leaders that hit the rate limit do not pin the panel to themselves; they wake peers, and hide/release also wakes so a focused follower can steal immediately. The challenge tab is not opened automatically — Open Cloudflare Challenge uses the lane's cached openUrl synchronously when the user clicks. Queue verification tabs are marker-only (brazen_hi / brazen_hi_ctx) and never claim processor leadership. A restore/watchdog path reclaims stale prompt ownership, clears lanes older than softExpireMs / default 2 minutes, and hides panels when a lane is cleared remotely. Done — resume clears that lane and wakes the leader. On the Phase-1 queue challenge media page itself, a secondary Done — resume — close tab performs the same clear/wake then window.close() (resume still succeeds if the browser blocks close). Cross-tab enqueue while blocked still writes queued rows; the blocked lane resumes after confirm or TTL clear. Both lanes may be blocked and visible at once under IndependentPolicy.

Download interruption prompt

Navigating the leader tab while GM_download is in flight leaves the browser transfer running but orphans the downloading row (inProgress was set just before GM_download). On the next visible full-UI page that steals leadership, those orphans are deferred (not immediately requeued) and #bv-download-interruption-panel shows a 5s timed button (thin end-to-end bar; hover pauses). Click marks the rows done and sets paused (stops the relaunch churn for a large ongoing file). Timeout requeues and relaunches (previous default). The download lane stays blocked while the decision is pending. Non-UI / background requeue contexts still flip orphans to queued as before.

Recommendation: for large downloads, keep a separate lightweight homepage open as the download leader tab and browse or pick media in other tabs so transfers are not interrupted by navigation.


Integration notes

Topic Detail
Grant @grant GM_download on this module (and typically on the app too)
Load order After Framework core; before app script
DOM resolveItem / progress slot / tile overlays use HTMLElement; page resolution uses native fetch + DOMParser
Dock Manager registers dock fields (selection, enqueue, start/pause, clear, progress, discovery). Apps pushField enable key on the rail.
Ledger Constructor downloadDuplicateLedger on Framework; claims happen in Download Manager — see Framework developer guide. GM_download always uses conflictAction: 'overwrite' (never uniquify) so user pattern names are never altered.
View Layer Progress slot, item progress overlays, discovery/human-interaction panels — View Layer
IndexedDB downloadResolutionQueue, downloadQueue, downloadManagerStateIndexedDB Storage

Public API reference (index)

  • Setup: configureDownloadManager, getDownloadManager
  • Queue: downloadImmediate, enqueueDownload, dequeueDownload, isQueued, toggleDownloadManagerPaused, clearDownloadQueue, toggleSelectionMode, toggleCurrentMediaQueued, paceResolutionInitiation
  • Discovery: confirmTagDiscoveryMappings, skipTagDiscoveryInclusion, openTagDiscoveryMedia, toggleTagDiscoveryMode, restoreTagDiscoveryPanelIfNeeded
  • Status: getDownloadManagerProgress, getDownloadQueueCount, isDownloadPageRole, isDownloadManagerEnabled, isDownloadManagerLeaderTab, isDownloadManagerRunning, requestDownloadManagerLeadership, handleMediaCloudflarePage, shouldSilenceMediaCloudflarePrompt, peekHumanInteractionBlockedMirror, peekHumanInteractionBlockedIdb, hasBrazenHumanInteractionMarker, isQueueOpenedVerificationTab, isHumanInteractionBlockedSync
  • Paths: buildDownloadPathFromPatterns, parseDownloadTagSubstitutionLines, buildDownloadTagSubstitutionMap, buildDownloadPath