Summary
This extension streamlines searching across user-hosted Servarr applications (Sonarr, Radarr, Lidarr, Readarr) by:
- Auto-prefilling Servarr search pages when a user navigates to a URL containing
/add/new/<term>(or similar). - Adding context menu items to search selected text directly in any configured Servarr instance.
- Injecting small Servarr search icons/links into supported third-party media sites (IMDb, TMDb, TVDb, Trakt, TVmaze, MusicBrainz, Letterboxd, TV Calendar, Rotten Tomatoes, Metacritic, Simkl, IPTorrents, Last.fm, Allociné, SensCritique, Betaseries, Prime Video, Rate Your Music, MyAnimeList, Wikipedia — see the integrations gallery).
- Providing an options UI where users configure base URLs + API keys (optional) for automatic advanced selector configuration.
- Backup & restore of user settings via a dedicated options tab: exports a JSON file using a schema-versioned envelope; imports either the new envelope or legacy plain JSON with validation; supports merge or replace modes and a preview-changes diff.
The extension does not collect analytics, ship remote code, or exfiltrate user data. All API calls target only the user's own Servarr instances explicitly configured in settings. See the latest release changes in the changelog.
Repository layout (source vs built artifacts)
| Path | Purpose |
|---|---|
src/ | Authoritative source (HTML, JS, Tailwind/Sass inputs, manifests). |
src/manifest-chromium/manifest.json | Chromium MV3 manifest. |
src/manifest-firefox/manifest.json | Firefox MV2 manifest (kept until MV3 is fully supported on Firefox for parity). |
src/content/js/ | Core runtime scripts (logic, content script, shared helpers, popup & options). |
src/content/css/ | Tailwind entry (tailwind.css). |
dist/chromium/ | Build output for Chromium (produced by grunt release). |
dist/firefox/ | Build output for Firefox. |
tests/playwright/ | Playwright integration tests (site integration smoke tests). |
tests/unit-tests/ | Jest unit tests (minimal). |
Gruntfile.js | Build pipeline definition. |
tailwind.config.js | Tailwind configuration (purge/content globs + safelist). |
All minified/processed assets in dist/ are reproducible by following the build steps below.
Build & verification steps
Perform a clean build from source (commands are cross-platform):
npm ci
grunt release
This produces:
dist/chromium(MV3 build usingeventPage.chrome.jsas service worker + background script bundle pieces).dist/firefox(MV2 build withbackground.scripts).
Mapping built code to source
| Built file (example) | Source origin |
|---|---|
dist/**/popup.html | src/popup.html (unchanged except path rewriting if any). |
dist/**/options.html | src/options.html. |
dist/**/content/js/*.js | Direct copies from src/content/js/ (no obfuscation). |
dist/**/content/css/tailwind.css | Generated by Tailwind from utility classes declared across src/**/*.html & src/**/*.js + safelist. |
| Icon images | Copied 1:1 from src/content/assets/images/. |
There is no custom packer, no dynamic code generation beyond Tailwind's static CSS expansion.
Permissions & justification
manifest-chromium/manifest.json:
"permissions": ["scripting", "storage", "activeTab", "contextMenus"],
"host_permissions": [
"*://*.allocine.fr/*", "*://*.betaseries.com/*", "*://*.imdb.com/*",
"*://*.iptorrents.com/*", "*://*.iptorrents.net/*", "*://*.iptorrents.me/*",
"*://*.iptorrents.eu/*", "*://*.iptorrents.ru/*", "*://*.last.fm/*",
"*://*.letterboxd.com/*", "*://*.metacritic.com/*", "*://*.musicbrainz.org/*",
"*://*.myanimelist.net/*", "*://*.primevideo.com/*", "*://*.rateyourmusic.com/*",
"*://*.rottentomatoes.com/*", "*://*.senscritique.com/*", "*://*.simkl.com/*",
"*://*.themoviedb.org/*", "*://*.thetvdb.com/*", "*://*.trakt.tv/*",
"*://*.pogdesign.co.uk/*", "*://*.tvmaze.com/*", "*://*.wikipedia.org/*"
],
"optional_host_permissions": ["<all_urls>"]
| Permission | Rationale |
|---|---|
scripting | Injection of content script on listed domains (MV3 requirement). |
storage | Persist user configuration (base URLs, API keys, toggles). |
activeTab | Allow immediate context-menu-initiated search to open the correct tab / access the tab URL when needed. |
contextMenus | Add right-click Servarr search items. |
| Host list | Integration discovery. The site list is explicit for transparency. |
<all_urls> (optional) | User-specified Servarr domains, unknown at install time (e.g. http://nas.local:8989). Users grant permission to their Servarr instances on a per-domain basis. |
Firefox (MV2)
manifest-firefox/manifest.json lists the same host patterns plus storage, activeTab, tabs and contextMenus under permissions (MV2 does not separate host_permissions), with <all_urls> under optional_permissions. The same justifications apply.
Background & runtime architecture
| Component | Role |
|---|---|
Content scripts (content_script.js + engine files) | Injected two ways: declaratively via the manifest content_scripts entry (run_at: document_idle) on the listed integration domains, and programmatically from the background for user-configured Servarr instance domains (unknown at install time). The same bundled, local files are used for both; the background path skips domains already covered declaratively to avoid double injection. |
eventPage.chrome.js | MV3 service worker (message routing, context menu handling, storage change propagation, and programmatic content-script injection for Servarr domains). |
eventPage.js | Firefox MV2 background equivalent. |
core.js | Shared helpers: settings access (storage), API version probing, URL building, small browser polyfill for non-WebExtension contexts (used in tests). |
options.js / options_*.js | Options UI assembly and tab-specific logic (settings load/save, site/integration list management, backup/restore handling). |
popup.js | Popup enable/disable toggle + quick settings link. |
All logic is synchronous or simple async, with fetch only to user-supplied Servarr endpoints (API base derived from settings). No external analytics endpoints.
Data handling & privacy
| Aspect | Details |
|---|---|
| Storage scope | browser.storage.sync / local (standard WebExtension) for configuration only. |
| Personal data | None collected or transmitted externally. |
| Network calls | Only to user Servarr instances (optional) when an API key is present — to detect version for auto configuration. |
| Telemetry / tracking / ads | None. |
| Backup/restore | Export produces a local JSON download only; import reads a local JSON file chosen by the user. No data is transmitted externally. |
| Firefox data consent | browser_specific_settings.gecko.data_collection_permissions is declared as required: ["none"], formally stating that no data is collected (Firefox built-in data consent). |
Third-party libraries
| Library | Source | Use |
|---|---|---|
| jQuery | node_modules/jquery | Simple DOM selection & legacy code compatibility. |
| Font Awesome 7 | @fortawesome/fontawesome-free | Icons in popup/options UIs. |
| Tailwind CSS 4 + Forms plugin | tailwindcss, @tailwindcss/forms | Utility-first styling, accessible form defaults. Built into a single CSS file. |
| Spectrum colour picker | spectrum-colorpicker2 | Colour picker. |
| webextension-polyfill | webextension-polyfill | Unified browser.* API shim across Chrome/Firefox. |
All libraries are installed via npm ci and bundled transparently (no CDN runtime fetches). Unminified sources remain in node_modules/.
How to functionally test
8.1 Load the extension (Chromium)
- Build:
npm ci && grunt release. - Open
chrome://extensions→ enable Developer Mode. - Click "Load unpacked" → select
dist/chromium. - Open the popup: verify the enable/disable button toggles state (stored in extension storage).
8.2 Load the extension (Firefox)
Option A (temporary install): build, visit about:debugging#/runtime/this-firefox, "Load Temporary Add-on" → pick any file inside dist/firefox (e.g. manifest.json).
Option B (web-ext run):
npm ci
grunt release
npm run firefox
8.3 Configure & test core features
| Feature | Steps |
|---|---|
| Auto-search injection | Navigate to a configured Sonarr/Radarr URL, append /add/new/<term> → search field prefilled & results loaded. |
| Context menu search | Select text on any page → right-click → choose a Servarr target → new tab opens to the correct add/new path. |
| Integration icons | Visit (e.g.) https://www.imdb.com/title/tt0944947/ → Servarr icon(s) should inject near the title region (depends on site layout). |
| Enable/disable toggle | Popup → toggle → integration icons & auto-search cease when disabled. |
| Advanced auto-detect | In options: set base URL + API key for (e.g.) Sonarr → advanced selectors auto-populate; test by removing the key and toggling detection off/on. |
| Custom icon (if enabled) | In options, navigate to the custom icon tab; enable & adjust position/colour; verify the preview updates. |
8.4 Storage inspection
In DevTools (background page or service worker):
await browser.storage.sync.get();
Review the structure (only configuration JSON under a single key).
8.5 Backup & restore
Options → "Backup & restore" tab.
- Backup: "Download backup" produces a pretty-printed JSON file using an envelope format —
type("servarr-autosearch-settings"),schemaVersion(1),exportedAt,appVersion, and adatapayload with internal fields (prefixed_/__) stripped. - Restore: choose a saved JSON file (envelope or legacy plain JSON — legacy accepted with an informational note). Optional replace mode overwrites fully; the default is a deep merge (arrays replaced; objects merged). "Preview changes" shows a diff of added/removed/changed paths before applying. Files are minimally validated (
configobject,sites[],integrations[]); a newerschemaVersionwarns before import, older schemas are normalised. - Reset: "Reset settings" → confirmation prompt → resets to built-in defaults.
- No network transmission occurs during backup or restore; files are handled locally in the browser context.
Security review checklist
| Check | Notes |
|---|---|
| Remote script injection | None (all scripts packaged locally). |
eval / dynamic function | Not used. |
| Dangerous DOM APIs | Only standard element creation & attribute manipulation; no unsanitised HTML injection from external sources. |
| External network endpoints | Only user-configured Servarr base URLs when an API key is present (GET/POST for version test). |
| Permission minimisation | Core set only; <all_urls> justified for unknown self-hosted domains & a consistent injection model. |
| CSP | Uses default WebExtension CSP; no inline JS reliance beyond standard HTML event-free patterns. |
Testing (automated)
Playwright (site integrations) — not required for functional store acceptance but provided for transparency:
npm ci
grunt release
cd tests/playwright
npm i
npx playwright install --with-deps chromium
npx playwright test
These load integration target pages and assert icon injection or URL logic (requires network access to public sites; may be flaky if site layouts change).
Jest (unit tests) — currently minimal; used as a development safeguard:
npm ci
npm test
Notable implementation details
| Concern | Handling |
|---|---|
| Accessible tabs | ARIA roles & keyboard navigation (ArrowLeft/Right, Home/End). |
| Toggle controls | Replaces the legacy Bootstrap toggle plugin with small custom button logic + aria-pressed. |
| Range inputs | Native <input type="range"> styled via the Tailwind forms plugin (no third-party slider). |
| Status badges (options) | Show passive health of Servarr API endpoints; timeouts gracefully degrade to "Unknown/Fail". |
| Browser polyfill | webextension-polyfill for consistent Promise-based APIs. |
How to trace code paths
Typical icon injection path on an integration page:
- Content script runs (
sonarr_radarr_lidarr_autosearch.js). - Loads settings via helpers in
core.js. - Detects page type via site-specific selectors.
- Builds search URLs and injects an
<a>or<img>element with a click handler. - If disabled (
settings.config.enabled === false) it returns early.
Auto-search path:
- User navigates to an add/new URL carrying trailing search text.
- Content script checks the path & extracts the trailing segment.
- Waits for the search input (configurable selectors) up to a timeout.
- Sets the value and dispatches an input event.
Known limitations / edge cases
| Area | Note |
|---|---|
| Layout changes on external sites | May break icon injection; handled by future config updates, not runtime heuristics. |
| Firefox MV3 migration | Pending full parity; the MV2 manifest is currently retained for Android support (see Firefox for Android MV3 compatibility). |
Reviewer quick script audit pointers
Search for any suspicious patterns:
eval(→ not present.new Function(→ not present.fetch(/XMLHttpRequest→ confined to Servarr API probing incore.js(search forcallApi).- Event listeners for the context menu: background script only (
eventPage.*). - Storage key:
sonarrRadarrLidarrAutosearchSettings(searchable).
Contact
If clarification is required during review, please raise an issue in the repository or include reviewer notes in the store submission feedback.
Summary assertion: all shipped code is deterministic & build-reproducible with npm ci && grunt release, free of remote executable payloads, limited to user-directed network activity, and structured for least complexity in review (no bundler obfuscation).
Thank you for reviewing this extension. 📡