MITupdated 15d ago
Fixes the "re-downloading the same show" symptom caused by Miatrix API key rotation. When Miatrix rotates its keys, Prowlarr's cached key goes stale and Miatrix starts returning duplicate or incorrect results — causing the Arr stack to re-queue already-downloaded episodes.
What can you do with Refresh Miatrix Token?
name: refresh-miatrix-token description: > Refresh the Miatrix indexer API token in Prowlarr by logging into Miatrix via browser automation and extracting the current API key. Use this skill whenever Miatrix keeps re-downloading the same episodes or shows, when Prowlarr reports Miatrix indexer errors or failures, or when a Miatrix API key rotation is suspected. The skill handles the full workflow automatically: gathers credentials, logs into Miatrix, grabs the API key from the profile page, and updates Prowlarr's indexer config — no manual steps required. category: Media tags: [prowlarr, miatrix, usenet, arr, indexer] status: stable
Refresh Miatrix Token
Fixes the "re-downloading the same show" symptom caused by Miatrix API key rotation. When Miatrix rotates its keys, Prowlarr's cached key goes stale and Miatrix starts returning duplicate or incorrect results — causing the Arr stack to re-queue already-downloaded episodes.
Step 1: Gather required credentials
You need four values before proceeding:
| Variable | Description |
|---|---|
MIATRIX_USER |
Miatrix username or email |
MIATRIX_PASS |
Miatrix password |
PROWLARR_URL |
Base URL of the Prowlarr instance (e.g. https://prowlarr.example.com) |
PROWLARR_KEY |
Prowlarr API key (Settings → General → API Key) |
Resolve them in this order of preference:
- Already set in the environment — check with
env | grep -E 'MIATRIX|PROWLARR' - OpenBao / Vault — if
/run/vault-agent/tokenexists, use it:
If the machine token file doesn't exist, tryexport VAULT_TOKEN=$(cat /run/vault-agent/token) # Path varies by project — check CLAUDE.md or .env.example for the kv path bao kv get -field=username secret/services/miatrix # example bao kv get -field=password secret/services/miatrix bao kv get -field=api_key secret/services/prowlarrBAO_TOKEN/VAULT_TOKENenv vars. - Other secrets manager — 1Password CLI, Bitwarden CLI,
pass, etc. Check the project'sCLAUDE.mdor.env.examplefor the path. - Ask the user — if credentials are not findable, ask for them directly. Never guess or construct them.
Once resolved, assign them:
MIATRIX_USER="<value>"
MIATRIX_PASS="<value>"
PROWLARR_URL="<value>" # no trailing slash
PROWLARR_KEY="<value>"
Never log, echo, or display credential values. If any value cannot be resolved, stop and report which one is missing.
Step 2: Log into Miatrix via Chrome DevTools
Open a fresh browser tab and navigate to the login page.
new_page → get the page_id
navigate_page(page_id, "https://miatrix.com/login")
wait_for(page_id, ["Login", "Username", "Email", "Password"])
take_snapshot(page_id)
Fill in the credentials and submit. Common selector patterns for the form:
- Username/Email:
input[name="username"],input[type="email"],#username,#email - Password:
input[type="password"] - Submit:
button[type="submit"],input[type="submit"]
fill(page_id, <username_selector>, MIATRIX_USER)
fill(page_id, <password_selector>, MIATRIX_PASS)
click(page_id, <submit_selector>)
wait_for(page_id, ["networkidle", "Dashboard", "Profile", "Welcome"])
Verify login succeeded: the URL should no longer be /login and a snapshot
should show the user's account name or a dashboard, not an error message. If
still on the login page, stop immediately and report:
"Miatrix login failed — verify MIATRIX_USER and MIATRIX_PASS"
Do not retry with modified credentials.
Step 3: Extract the API key from the profile page
navigate_page(page_id, "https://miatrix.com/profile")
wait_for(page_id, ["networkidle", "API", "Key", "Token"])
take_snapshot(page_id)
The API key is visible as a text value or readonly input labelled "Site
Api/Rss Key" or similar. Try extracting it with evaluate_script:
(function() {
const selectors = [
'input[name*="api"]', 'input[id*="api"]',
'input[name*="key"]', 'input[id*="key"]',
'input[name*="token"]',
'.api-key', '.apikey', '[data-apikey]', '[data-api-key]',
];
for (const sel of selectors) {
const el = document.querySelector(sel);
if (el) return el.value || el.textContent.trim();
}
// Fall back: scan code/pre blocks for a plausible API key string
for (const el of document.querySelectorAll('code, pre, .token, .key')) {
const text = el.textContent.trim();
if (text.length > 16 && /^[a-zA-Z0-9_\-]+$/.test(text)) return text;
}
return null;
})()
If the script returns null, read the snapshot — the key may be in a button-revealed modal or "copy" widget. Click a "Show" or "Reveal" button first, then re-evaluate. If you still cannot extract it, take a screenshot and report the page structure so the selector can be updated.
Store the result as NEW_API_KEY. Close the browser tab when done:
close_page(page_id)
Step 4: Update Prowlarr's Miatrix indexer
Find the Miatrix indexer(s):
curl -s -H "X-Api-Key: $PROWLARR_KEY" \
"$PROWLARR_URL/api/v1/indexer" \
| python3 -c "
import sys, json
indexers = json.load(sys.stdin)
for i in indexers:
name = i.get('name','').lower()
defn = i.get('definitionName','').lower()
if 'miatrix' in name or 'miatrix' in defn:
print(i['id'], i['name'])
"
If no indexer is found, list all indexer names and report — the name in Prowlarr may differ from "Miatrix".
For each Miatrix indexer found, GET the full config, patch the API key field, and PUT it back. Use temp files to keep the shell pipeline simple:
# Clean up temp files on exit (even on error)
trap 'rm -f /tmp/prowlarr_indexer.json /tmp/prowlarr_patched.json' EXIT
# GET full config
curl -sf -H "X-Api-Key: $PROWLARR_KEY" \
"$PROWLARR_URL/api/v1/indexer/$INDEXER_ID" \
> /tmp/prowlarr_indexer.json
# Patch the apiKey field (commonly named apiKey, key, api_key, passkey, or token)
# Single-quoted PYEOF suppresses shell expansion; key passed as argv[1] to avoid injection
python3 - "$NEW_API_KEY" <<'PYEOF' > /tmp/prowlarr_patched.json
import json, sys
with open('/tmp/prowlarr_indexer.json') as f:
data = json.load(f)
new_key = sys.argv[1]
key_fields = {'apiKey', 'key', 'api_key', 'passkey', 'apikey', 'token'}
updated = False
for field in data.get('fields', []):
if field.get('name', '').lower() in key_fields:
field['value'] = new_key
updated = True
break
if not updated:
print('Available fields:', [f['name'] for f in data.get('fields', [])], file=sys.stderr)
sys.exit(1)
with open('/tmp/prowlarr_patched.json', 'w') as out:
json.dump(data, out)
PYEOF
# PUT back and capture HTTP status
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X PUT \
-H "X-Api-Key: $PROWLARR_KEY" \
-H "Content-Type: application/json" \
-d @/tmp/prowlarr_patched.json \
"$PROWLARR_URL/api/v1/indexer/$INDEXER_ID")
echo "Prowlarr responded: $HTTP_STATUS"
A 200 or 202 response means success. If the Python script exits with error
(field not found), print the available field names so the skill can be updated
with the correct field name.
Step 5: Verify and report
Test the updated indexer directly and check isValid:
curl -sf -X POST \
-H "X-Api-Key: $PROWLARR_KEY" \
"$PROWLARR_URL/api/v1/indexer/$INDEXER_ID/test" \
| python3 -c "import sys,json; r=json.load(sys.stdin); print('isValid:', r.get('isValid', r))"
Report the outcome clearly:
✓ Retrieved new Miatrix API key from https://miatrix.com/profile
✓ Updated Prowlarr indexer "<name>" (ID: N) at <PROWLARR_URL>
→ Prowlarr responded: 202
→ Indexer test: passed
On any failure, state the exact step that failed and the error. Do not include credential values in the output.
Install
Add Refresh Miatrix Token to your client. Pick the one you use.
npx skills add joestump/claude-skillsInstalls every skill in the repository, then prompts for which to keep.
/plugin marketplace add joestump/claude-skillsAdds the repository as a plugin marketplace; install individual plugins with `/plugin install`.
git clone https://github.com/joestump/claude-skills
cp -r refresh-miatrix-token ~/.claude/skills/A skill is a plain directory. Copy it into `.claude/skills/` in a project or in your home directory.
Score
79 / 100
Good