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/1909491/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
4.0.0
Created
2026-07-15
Updated
2026-08-22
Size
284 KB
License
GPL-3.0-only

Brazen Framework — Download Manager (developer guide)

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

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

Normative Reactor write/read paths: reactor-native-v2.spec.md.


When to use / when not to use

  • Use when your app needs cross-tab download queues, search-tile selection enqueue, 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.
  • Requires Reactor — one tab holds the coordinator Web Lock; pipeline work and authoritative IDB writes run there. Follower tabs use Reactor Commands and the dm.state.* patch stream for live UI.

Quick start

Load order (after Framework core):

// @require … Brazen Framework - Reactor.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'],
      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. Framework then calls afterDockReady() and, after ruleset hydrate, afterBootHydrate().


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, interleaved lane dispatcher: one serial regime, any-lane HI pauses both lanes, download may run while tag discovery gates resolution. Default false (independent lanes): concurrent resolution/download loops; tag discovery pauses resolution only; each lane's human-interaction block stops only that lane. 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-clear-download-queue only — coordinator crown is on the main dock rail), 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
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_DEFER_TAG_DISCOVERY defer-tag-discovery-unattended Defer Tag Discovery Until Resolution Completes

Mount all four via Framework createBehavioursTabPanel() (alphabetical title order). Mount download-path fields via createDownloadsTabPanel() (excludes selection-mode default).

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
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
tagDiscoveryToggle Dock tag-discovery mode button
dashboard Dedicated control / future-analytics host. Does not grant selection, enqueue, or tag-discovery toggle by itself. Does not auto-claim coordinator role. 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. ['mediaPost']
tagTypes Panel grouping: { label, rowClass, color } per type — colors via View Layer applyTagDiscoveryTypeColors. Auto-synced from filename/subfolder pattern pins on init and config change.
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 (typed-but-undiscovered tags remain unknown). Bulk reset: Framework resetAllTagsDiscovered() — see tagging.spec.md
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 a visible tab that steals panel ownership. 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; 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). Cross-tab sync uses Reactor Commands and dm.state.* patches — followers restore panels when gate fields change without visiting the coordinator tab. Hiding or unloading a tab releases panel ownership (not the queue gate) so another search tab can restore the panel; local DOM may stay mounted on blur (no flash on return) — restore re-steals ownership and force-shows the slide panel when review is still pending.

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 coordinator tabs leave promptTabId unset and wake peers via pipeline-pump so the focused tab can steal. Does not auto-open the challenge tab; Open reads the lane openUrl from readDmState() 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 on lane loops and HI watchdog (including hidden coordinators; 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 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
enqueueDownload(context) Add to resolution queue via Reactor Command; 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)
selectAllVisibleItems() Enqueue all visible search tiles not already in the pipeline (Selection Mode slide-out)
sendCommand(command) Low-level Reactor Command (coordinator kernel dispatch; follower bus + optimistic patches)

Reactor coordinator

Method Role
isCoordinator() This tab owns the Web Locks script coordinator role
requestCoordinatorRole({ steal?, ifAvailable? }) Steal or opportunistically claim coordinator; no idle-only alert. Same-tab reload: sessionStorage coordinator-sticky preserves prior identity
readDmState() Sync read via dm.state.snapshot signal atom

Also available on Framework: isCoordinator(), requestCoordinatorRole().

Tag discovery

Method Role
confirmTagDiscoveryMappings() Mark tags discovered (isDiscovered); promote types when unset; promote resolution item
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
isDownloadManagerRunning() Pipeline active (queues, blocked state, or in-tab processing)

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>
patternIncludesChip(pattern, chip) True when pattern references a download-path chip
registerSelectionDragGuard() Capture-phase drag guard while selection mode is active

Processors and coordinator role

One coordinator tab (Web Lock via Reactor) owns pipeline work. Resolution and download run through the lane dispatcher (_runLaneProcessor ×2 when linkQueues: false, _runInterleavedDispatcher when linkQueues: true). Public enqueue/dequeue/toggle/clear build Reactor Commands → kernelWriteThrough. Coordinator-only dm.state gate mutations use _commitCoordinatorDmState; followers apply dm.state.* patches and may send claim Commands for panel ownership.

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 lanes: stops resolution only (downloads continue). Interleaved: gates resolution; 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 coordinator 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.
Coordinator role Web Locks script coordinator (isCoordinator() / requestCoordinatorRole()). Crown on main dock rail (DOCK_COORDINATOR). Followers wake coordinator via pipeline-pump Commands. Same-tab reload reclaims via sessionStorage sticky + one-shot steal after kernel.start().
Cross-tab UI Followers subscribe to Reactor bus; _requestFollowerSnapshot() opens the patch stream on cold start. Dock progress reads signal atoms; coalesced timer paints (16ms) run on background tabs.
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).
Ledger Duplicate claim at download time in Download Manager when Skip or Hide is on; ledgerClaimed on download rows prevents double-claim after coordinator handoff.
UI cadence One coalesced dock paint per tick; _syncFromStorage on init, enqueue, Start, and follower patch batches. Pending counts use signal atoms on the paint hot path.

Recommendation: for large downloads, keep a dedicated tab as the coordinator 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 and Reactor; 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. Coordinator crown is on the main rail, not the Start/Pause slide-out.
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: enqueueDownload, dequeueDownload, isQueued, toggleDownloadManagerPaused, clearDownloadQueue, toggleSelectionMode, selectAllVisibleItems, toggleCurrentMediaQueued, paceResolutionInitiation, sendCommand
  • Coordinator: isCoordinator, requestCoordinatorRole, readDmState
  • Discovery: confirmTagDiscoveryMappings, skipTagDiscoveryInclusion, openTagDiscoveryMedia, toggleTagDiscoveryMode, restoreTagDiscoveryPanelIfNeeded
  • Status: getDownloadManagerProgress, getDownloadQueueCount, isDownloadPageRole, isDownloadManagerEnabled, isDownloadManagerRunning, handleMediaCloudflarePage, shouldSilenceMediaCloudflarePrompt, peekHumanInteractionBlockedMirror, peekHumanInteractionBlockedIdb, hasBrazenHumanInteractionMarker, isQueueOpenedVerificationTab, isHumanInteractionBlockedSync
  • Paths: buildDownloadPathFromPatterns, parseDownloadTagSubstitutionLines, buildDownloadTagSubstitutionMap, buildDownloadPath, patternIncludesChip, registerSelectionDragGuard