NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
PSPrices-Collection-Live-Search.user.js is a Tampermonkey userscript that adds cached live substring search to PSPrices avatar and theme collection pages across regions, indexing paginated collection results beyond the current visible page.
Current documented release: 1.0.34.
Go To Themes / Go To Avatars shortcut beside the native collection headingPS3, PS4, and PS5Free only checkbox that can combine with text and platform filtersThe userscript loads on regional PSPrices pages so background indexing can start before the user opens the collection page:
https://psprices.com/region-*
https://www.psprices.com/region-*
The visible search UI mounts only on the canonical collection endpoints:
https://psprices.com/region-*/collection/avatars
https://psprices.com/region-*/collection/themes
Equivalent www.psprices.com routes are also covered.
The UI intentionally does not mount on the old PSPrices filter endpoints:
/collection/free-avatars
/collection/ps3-avatars
/collection/ps4-avatars
/collection/free-themes
/collection/ps3-themes
/collection/ps4-themes
It also does not mount on collection URLs containing a platform query parameter, such as:
/collection/themes?platform=iOS%2CPS3
Those routes are left to PSPrices' native page behavior.
/collection/avatars or /collection/themes.The search box performs case-insensitive substring matching against cached item text.
Typing:
batman
matches item text containing batman anywhere in the title or hydrated searchable text.
Typing a short partial query such as:
ba
matches cached item text containing ba anywhere in the string.
Multi-word queries are handled in two passes:
Search runs with a small input debounce so fast typing does not rebuild the result grid on every key event.
The native PSPrices collection grid and native pagination are hidden on the canonical mounted routes. The userscript keeps its custom result grid empty while the current region cache is still building.
After the current region's avatar and theme caches are both complete, an empty query with All platforms selected renders the first 108 indexed items sorted alphabetically. Typing in the search box or enabling platform/free filters narrows that same sorted result set.
Search, platform/free filters, Show more, the result grid, and product-page detail hydration stay locked until the current region's avatar and theme caches are both 100% complete. Nothing populates in the custom grid while the user is still waiting on those caches. If the user stays on the page, the grid automatically populates when both caches hit 100%; a refresh is not required. This lock is still region-scoped: AU unlocks only after AU avatars and AU themes finish, while another region has its own queued or paused cache state. Clearing the current region cache also clears the grid until both rebuilt caches finish.
Text-query changes can keep matching partial results on screen briefly while the next live result set hydrates. Platform and Free only changes are treated as hard filter changes: the visible grid is rebuilt from scratch, the render limit resets to 108, and in-flight detail hydration is abandoned for the previous filter state.
Leading punctuation is ignored for sorting. Result order is:
A-Z0-9The userscript replaces reliance on PSPrices' separate collection filter URLs with local filters:
All platformsPS3PS4PS5Free onlyThe platform dropdown and Free only checkbox can be combined. For example, selecting PS4 and enabling Free only shows only free PS4 matches from the current collection's cached results.
The filter data is kept small in the stored index. Product page details are hydrated live for visible matched results, and platform/free filters can also hydrate unknown matching candidates in small batches so unconfirmed rows are not rendered as final results.
The script starts background indexing for the current region as soon as it runs on a regional PSPrices page.
For each region, the background worker indexes:
/collection/avatars
/collection/themes
The old filtered endpoints are not separately cached. Platform and free filters are derived from the indexed collection data and live result hydration.
Indexing runs in page chunks. A completed page is written to persistent browser storage immediately, and incomplete or failed pages are retried later instead of being treated as valid cache.
The UI status line mirrors the background worker when the search UI is visible. Example states include:
Indexing Avatars (AU)... 648 items from 6 / 149 pages.
Indexed cached pages. 2293 items from 64 / 64 pages.
Queued Themes (US); Avatars (AU) is indexing. 0 items from 0 / 1 page.
Paused: PSPrices returned a bot-protection or rate-limit page.
The progress bar tracks combined current-region cache progress across both canonical collections, so it reaches the end only after both avatars and themes are fully cached. It refreshes from local worker state and cache metadata signalled by other tabs.
If the user navigates away from the mounted collection page but stays on PSPrices, indexing continues in the background. If the regional route disappears, the worker waits for a grace period before pausing.
Caches are scoped per host, region, and collection. Changing from region-au to region-us starts or resumes a separate regional cache.
Only one background prewarm worker is intended to run across open PSPrices tabs at a time. The script still uses small localStorage lease records for a best-effort cross-tab lock, even when the main cache backend is IndexedDB:
If a tab unloads while indexing, it broadcasts a short stop signal so other active tabs can pause and retry cleanly instead of duplicating work immediately.
The index uses browser IndexedDB by default because it has a much larger practical quota than localStorage. If IndexedDB cannot open or write, the script falls back to the legacy localStorage cache backend.
The active backend is logged in the browser console:
PSPrices Collection Live Search: Collection-search cache storage backend. IndexedDB active
To force the legacy backend for testing, set this near the top of the userscript:
const CACHE_FORCE_LOCAL_STORAGE = true;
The script stores a backend marker. If the marker changes between IndexedDB and localStorage, the script first tries to move the current region's compatible cache entries into the new backend, then purges the old backend's collection-search cache data. The old-backend purge is global across regions and old versioned cache scopes, but the small cross-tab lease records remain in localStorage. Moving into localStorage only carries the compact current-region collection cache for avatars and themes; IndexedDB-only product detail metadata is not copied because the localStorage backend does not use it. If the current-region move into localStorage is incomplete because browser quota is already full, the script skips purging the old IndexedDB cache so data that did not fit is not deleted. If the storage backend changes at the same time as an incompatible CACHE_SCHEMA_VERSION change, the script skips moving old cache data and prefers a fresh rebuild.
If IndexedDB opens but later fails a write, the script switches to localStorage fallback for the active session and copies the current region's in-memory cache snapshot into localStorage where possible. It only starts a best-effort purge of stale IndexedDB cache data if that localStorage copy completes; partial copies keep the old IndexedDB data in place. If cleanup fails, the fallback session still continues and logs the cleanup problem.
Cache keys and database entries use the script prefix:
psprices-live-search:
The stored collection page data is intentionally compact. It primarily stores:
When IndexedDB is active, the script also keeps a separate product detail metadata cache for visible/hydrated items and metadata already present on collection pages. That detail cache can include image URLs, price text, platform text, extra searchable text, and filter flags. It stores image URLs, not image binary blobs, so browser HTTP cache still owns the actual image files.
When localStorage is forced or used as fallback, the cache stays compact and does not store the heavier detail metadata. In that mode, thumbnails, prices, and detailed platform text are fetched live for visible matched results when needed.
The clear-cache button only clears the current region:
Clear Cache (AU)
After clearing, background indexing starts again for that region.
The main cache freshness constants are near the top of the userscript:
const CACHE_TTL_MS = 14 * 24 * 60 * 60 * 1000;
const DETAIL_CACHE_TTL_MS = 14 * 24 * 60 * 60 * 1000;
const CACHE_REVALIDATE_MS = 12 * 60 * 60 * 1000;
const CACHE_SCHEMA_VERSION = 10;
const CACHE_RESET_ON_SCHEMA_CHANGE = true;
Their purposes are:
CACHE_TTL_MS: maximum age of a cached page before it is removed and rebuiltDETAIL_CACHE_TTL_MS: maximum age of IndexedDB product detail metadata before it is refetchedCACHE_REVALIDATE_MS: age after which an otherwise complete collection can be checked againCACHE_SCHEMA_VERSION: manual cache migration marker for incompatible cache changesCACHE_RESET_ON_SCHEMA_CHANGE: when true, changing the schema version purges old script caches on next loadWhen CACHE_SCHEMA_VERSION changes, the script removes previous collection-search cache entries from the active backend and stores the new migration marker. This prevents stale incompatible cache formats from consuming space or causing wrong results.
The script tracks its own approximate cache usage. The legacy localStorage fallback keeps the older low cap so it stays under common browser quotas:
const CACHE_LOCAL_MAX_BYTES = 4.4 * 1024 * 1024;
const CACHE_LOCAL_TARGET_BYTES = 3.4 * 1024 * 1024;
The IndexedDB backend uses larger soft and hard thresholds:
const CACHE_INDEXEDDB_WARN_BYTES = 2 * 1024 * 1024 * 1024;
const CACHE_INDEXEDDB_MAX_BYTES = 4 * 1024 * 1024 * 1024;
const CACHE_INDEXEDDB_TARGET_BYTES = 3.5 * 1024 * 1024 * 1024;
const CACHE_INDEXEDDB_WRITE_BATCH_SIZE = 8;
const CACHE_INDEXEDDB_WRITE_FLUSH_MS = 250;
If the script cache grows too large, it prunes older script-owned region caches first. It tries to avoid purging the active region currently being used.
Browser quota is still controlled by the browser and userscript manager environment. If IndexedDB writes fail, the script switches to localStorage fallback when possible. If persistent storage is unavailable, the UI and console report cache write failures, and already indexed in-memory data can still be used during the current page session.
IndexedDB writes update the in-memory cache immediately, then flush to disk in small batches. The batcher reduces transaction overhead while keeping the unflushed window short. It flushes when the batch reaches CACHE_INDEXEDDB_WRITE_BATCH_SIZE, after CACHE_INDEXEDDB_WRITE_FLUSH_MS, or best-effort during page unload. If the browser closes the last tab before the unload flush finishes, any unflushed chunks simply rebuild later from the last completed cache page.
This userscript can be network-heavy. Cache indexing walks the collection pages with concurrent requests, and live detail hydration can fetch product pages for the currently visible or filter-confirmation candidates. The defaults are intended to be moderately fast, but lowering the concurrency or adding delay is the safest option if PSPrices starts responding slowly, shows bot protection, or rate-limits the browser session.
Collection indexing uses these main knobs:
const FETCH_CONCURRENCY = 6;
const FETCH_RETRY_COUNT = 1;
const FETCH_TIMEOUT_MS = 30000;
const FETCH_DELAY_MS = 800;
const FETCH_JITTER_MS = 500;
const MAX_HARD_FAILURES = 2;
Their purposes are:
FETCH_CONCURRENCY: number of collection pages fetched in parallel for foreground indexingFETCH_RETRY_COUNT: retry attempts for a failed page requestFETCH_TIMEOUT_MS: maximum time allowed for each fetchFETCH_DELAY_MS: base wait before page fetchesFETCH_JITTER_MS: random extra wait added to avoid perfectly synchronized requestsMAX_HARD_FAILURES: hard failure threshold before indexing pausesBackground prewarm uses:
const PREWARM_FETCH_CONCURRENCY = 6;
const BACKGROUND_LOOKAHEAD_QUEUE_MULTIPLIER = 1;
const BACKGROUND_LOOKAHEAD_MIN_EXTRA_PAGES = 2;
const PREWARM_COLLECTION_DELAY_MS = 1500;
const PREWARM_CONTEXT_GRACE_MS = 60 * 1000;
const PREWARM_LEASE_HEARTBEAT_MS = 5000;
const PREWARM_LEASE_STALE_MS = 30 * 1000;
Their purposes are:
PREWARM_FETCH_CONCURRENCY: page fetch concurrency for background region indexingBACKGROUND_LOOKAHEAD_QUEUE_MULTIPLIER: scales the unknown-page background queue from the worker count before the extra-page buffer is appliedBACKGROUND_LOOKAHEAD_MIN_EXTRA_PAGES: minimum extra unknown pages kept ahead of the worker countPREWARM_COLLECTION_DELAY_MS: pause between avatar and theme collection prewarm passesPREWARM_CONTEXT_GRACE_MS: time to wait after losing the regional page context before pausingPREWARM_LEASE_HEARTBEAT_MS: how often an indexing tab refreshes its cross-tab leasePREWARM_LEASE_STALE_MS: how long before another tab can treat a lease as abandonedWhen the total page count is unknown, the background worker speculatively queues pages ahead of the last discovered page. A missing lookahead page such as a 404/410 response is treated as the collection end marker; the previous page remains queued and must be fetched before the cache completes.
With the defaults, unknown-page prewarm keeps the queue at PREWARM_FETCH_CONCURRENCY + BACKGROUND_LOOKAHEAD_MIN_EXTRA_PAGES, or 8 pages total: 6 active workers plus 2 pages ahead. Increase BACKGROUND_LOOKAHEAD_MIN_EXTRA_PAGES if workers drain the queue too quickly; lower it if you want fewer speculative requests near the final page.
If PSPrices returns a rate-limit, bot-protection, or challenge-like page, indexing pauses and writes a console warning. A paused background worker can retry when the user types into the search box, subject to this cooldown:
const PAUSED_SEARCH_RESUME_COOLDOWN_MS = 60 * 1000;
The script does not render every indexed item or match at once on large collections. It starts with a practical sorted batch and expands on demand:
const INITIAL_RENDER_LIMIT = 108;
const RENDER_STEP = 54;
const MAX_RENDER_LIMIT = -1;
Their purposes are:
INITIAL_RENDER_LIMIT: number of custom collection results shown first, including the empty-query default viewRENDER_STEP: number of additional results added by each Show more clickMAX_RENDER_LIMIT: hard cap for rendered results; the default -1 means no hard capSet MAX_RENDER_LIMIT to -1 for no hard cap. This can be heavy on low-memory machines.
When a result set exceeds the hard cap, the UI reports that only part of the match list is shown and suggests refining the search.
The cache keeps the stored index small. Detail hydration waits until the current region's avatar and theme caches are both 100% complete. During cache builds, the grid stays empty and no product-page URL fetches are started, avoiding extra product requests on top of collection-page cache indexing.
When PS3, PS4, PS5, or Free only filters are active, compact cached rows with unknown platform or price data are checked against IndexedDB detail metadata first. Product pages are fetched only when that detail metadata is missing, stale, or still incomplete. If the search box is empty, candidate checks are limited to the current render window, starting at 108 sorted items and expanding only when Show more is clicked. Once text is typed, that query builds the broad candidate pool while platform and free filters trim confirmed matches from it.
While a visible result batch is hydrating, partial re-renders keep that batch's hydration queue stable. Search text, filter, or Show more changes still cancel and retarget hydration so stale result details are not fetched longer than needed. When the active metadata worker batch drains, the grid is forced through one final render so completed theme details cannot stay hidden behind a pending debounce. If compact cached rows are reloaded without metadata, they can be hydrated again even when their key was fetched earlier in the same page session. If IndexedDB has fresh detail metadata, hydration can complete from cache without a product-page request.
Result cards use detail-confirmed rendering, including blank All platforms views, so grids fill progressively with real thumbnails, prices, and platform badges instead of painting a full page of placeholders first. All platforms is treated as a PS3/PS4/PS5 union while compact rows are being confirmed. On collection page launch, initial hydration waits for the page load event and mounted userscript UI before restarting, and retries if the grid stays empty. Product detail rows that fail hydration are not rendered for avatars or themes; the status line reports how many matching rows failed metadata fetching.
While candidate hydration, same-region collection cache indexing, or queued same-region lease work is still running, the UI labels already verified matches as confirmed results and shows a small pulsing indicator beside that status. The indicator is refreshed from cache status updates as well as result renders, so the avatar page can pulse while the theme cache builds and the theme page can pulse while the avatar cache builds, including after page reloads or region navigation. Remaining undisplayed items are reported through the Show more button.
The main controls are:
const LIVE_DETAIL_HYDRATION_ENABLED = true;
const LIVE_DETAIL_FETCH_CONCURRENCY = 8;
const LIVE_DETAIL_FETCH_DELAY_MS = 0;
const LIVE_DETAIL_FETCH_JITTER_MS = 0;
const LIVE_DETAIL_RENDER_DEBOUNCE_MS = 15;
const LIVE_DETAIL_MAX_ITEMS_PER_RENDER = -1;
const LIVE_DETAIL_FILTER_CANDIDATE_BATCH = 108;
Their purposes are:
LIVE_DETAIL_HYDRATION_ENABLED: enables product-page detail fetching for rendered results and filter candidate checksLIVE_DETAIL_FETCH_CONCURRENCY: number of product pages fetched in parallelLIVE_DETAIL_FETCH_DELAY_MS: base delay before each live detail fetchLIVE_DETAIL_FETCH_JITTER_MS: random extra delay for live detail fetchesLIVE_DETAIL_RENDER_DEBOUNCE_MS: small delay before applying hydrated card updatesLIVE_DETAIL_MAX_ITEMS_PER_RENDER: maximum visible rendered cards to hydrate, or -1 for no capLIVE_DETAIL_FILTER_CANDIDATE_BATCH: maximum unknown platform/free candidate rows checked per filter pass, or -1 for no capHydration is retargeted as the query changes. Results that still match the new query can remain on screen, while clearly stale result cards are removed after:
const RENDER_STALE_RESULT_GRACE_MS = 2500;
This avoids repeatedly clearing and rebuilding the entire grid while the user is typing quickly.
The userscript runs at document-start so selected native PSPrices controls can be hidden before they paint:
/collection/avatars, it hides the native tablist block and native Likes/Filter controls/collection/themes, it hides the native platform stripe and native Likes/Filter controlsdata-psprices-live-search-hidden="true" are hidden with bootstrap CSSThe avatar and theme Likes/Filter action row hides are scoped to the collection main content and the native filter toggle, so they do not hide the PSPrices header/home block. The theme platform stripe hide remains scoped to the route class for /collection/themes only. These hides do not apply on product pages or /collection/themes?platform=... query-filter URLs.
Logging is controlled near the top of the userscript:
const LOG_LEVEL = 'info';
Supported values:
'info': startup, route changes, active cache backend, cache state, pause reasons, storage failures, and final indexing results'verbose': detailed route parsing, cache reads and writes, request lifecycle, parser counts, stale checks, and search result countsLogs are prefixed with:
PSPrices Collection Live Search:
On startup, the default info log includes the userscript version in the same format as the other PSPrices scripts:
PSPrices Collection Live Search: has started (v1.0.34)
Logging is designed not to include cookies, credential headers, full response bodies, session data, or raw storage payloads.
This userscript can run alongside:
The collection live-search script primarily owns the canonical avatar/theme collection pages and product-page fetches used for visible result hydration. The checkout script primarily owns supported product-page purchase targets, and the SKU script owns the product-page SKU panel. These ownership boundaries let the three PSPrices userscripts coexist without intentionally replacing each other's UI.
The UI status line reports failed pages, queued indexing, paused indexing, and cache write problems when they occur.
Common failure modes include:
Incomplete pages are not treated as valid finished cache. On the next run, the script resumes from completed pages and rebuilds missing, stale, or corrupt chunks.
Rating: 0