app
Adom Chip Fetcher
Public Made by Adomby adom
Your whole parts library — manufacturer-grade chip CAD (symbol, footprint, 3D) one tap from your EDA tool.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
#!/bin/bash
# cf-ul-pro-batch.sh — UL Pro batch fetcher for ONE MPN.
# Requires: chip-fetcher pup session signed in to app.ultralibrarian.com
# (cookie persists; verify via `chip-fetcher login ultralib` if not).
#
# Usage:
# ./cf-ul-pro-batch.sh <MPN>
#
# Posts activity updates to /api/activity throughout so the dashboard
# shows real-time progress.
set -u
MPN="${1:?MPN required}"
SESSION="chip-fetcher"
ACTIVITY="http://127.0.0.1:8786/api/activity"
CF=/home/adom/project/chip-fetcher
post_activity() {
curl -s -X POST "$ACTIVITY" -H "Content-Type: application/json" \
-d "{\"mpn\":\"$MPN\",\"stage\":\"$1\"}" > /dev/null
}
clear_activity() {
curl -s -X POST "$ACTIVITY" -H "Content-Type: application/json" -d '{}' > /dev/null
}
post_activity "searching UL Pro"
# Step 1: Search UL Pro for the MPN, get part-page URL
adom-desktop browser_navigate "{\"sessionId\":\"$SESSION\",\"tabId\":\"tab-1\",\"url\":\"https://app.ultralibrarian.com/search?queryText=${MPN}\"}" >/dev/null 2>&1
sleep 5
URL=$(adom-desktop browser_eval "{\"sessionId\":\"$SESSION\",\"tabId\":\"tab-1\",\"expr\":\"document.querySelector('a[href*=details]')?.href || ''\"}" 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('result','').strip())")
if [[ -z "$URL" ]]; then
post_activity "ERROR: not found on UL Pro"; sleep 2; clear_activity
echo "ERROR: $MPN not found on UL Pro"; exit 1
fi
echo " url: $URL"
# Step 2: Navigate to part page
post_activity "loading part page"
adom-desktop browser_navigate "{\"sessionId\":\"$SESSION\",\"tabId\":\"tab-1\",\"url\":\"$URL\"}" >/dev/null 2>&1
sleep 4
# Step 3a: Click the BUTTON "Download Now" first to open the format picker.
# (UL Pro's flow: BUTTON expands picker → user ticks formats → A.export-trigger submits.)
post_activity "opening format picker"
adom-desktop browser_eval "{\"sessionId\":\"$SESSION\",\"tabId\":\"tab-1\",\"expr\":\"(()=>{var btn=Array.from(document.querySelectorAll('button')).find(b=>b.textContent.trim()==='Download Now' && b.offsetParent!==null); if(btn){btn.click(); return 'opened';} return 'no-button';})()\"}" >/dev/null 2>&1
sleep 2
# Step 3b: Tick Altium Designer + Fusion360 PCB + any consent checkboxes (now in the open picker)
post_activity "selecting Altium + Fusion formats"
TICKED=$(adom-desktop browser_eval "{\"sessionId\":\"$SESSION\",\"tabId\":\"tab-1\",\"expr\":\"(()=>{var labels=['Altium Designer','Fusion360 PCB']; var ticked=[]; document.querySelectorAll('input[type=checkbox]').forEach(cb=>{var lbl=cb.labels?.[0]?.textContent.trim()||''; if(labels.indexOf(lbl)>=0||/consent/i.test(cb.name||'')){cb.checked=true; cb.dispatchEvent(new Event('change',{bubbles:true})); ticked.push(lbl||cb.name);}}); return ticked.length;})()\"}" 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('result',''))")
echo " ticked: $TICKED checkboxes"
sleep 1
# Step 4: Mark timestamp BEFORE the click, then click the visible
# A.export-trigger Download Now anchor (actual submit). Uses unix seconds —
# adom-desktop v1.5+ accepts that for `since`.
BEFORE=$(date +%s)
post_activity "submitting download request"
CLICK=$(adom-desktop browser_eval "{\"sessionId\":\"$SESSION\",\"tabId\":\"tab-1\",\"expr\":\"(()=>{var btn=Array.from(document.querySelectorAll('a.export-trigger')).find(b=>b.textContent.trim()==='Download Now' && b.offsetParent!==null); if(!btn) btn=Array.from(document.querySelectorAll('button')).find(b=>b.textContent.trim()==='Download Now' && b.offsetParent!==null); if(!btn) return 'no-btn'; btn.click(); return btn.tagName;})()\"}" 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('result',''))")
if [[ "$CLICK" == "no-btn" || -z "$CLICK" ]]; then
post_activity "ERROR: submit button not found"; sleep 2; clear_activity
echo "ERROR: A.export-trigger Download Now not found"; exit 2
fi
echo " submitted via: $CLICK"
# Step 5+6: Watch Downloads for new ul_*.zip and pull it in one shot via
# the new desktop_pull_glob — no PowerShell, no shell_execute, no user prompt.
post_activity "downloading from UL Pro"
PULL=$(adom-desktop desktop_pull_glob "{\"path\":\"%USERPROFILE%\\\\Downloads\",\"glob\":\"ul_*.zip\",\"since\":$BEFORE,\"wait\":true,\"timeoutMs\":120000,\"saveTo\":\"$CF/incoming/\"}" 2>/dev/null)
ZIP=$(echo "$PULL" | python3 -c "import sys,json; d=json.load(sys.stdin); f=(d.get('files') or [{}])[0]; print(f.get('path',''))")
NEW=$(basename "$ZIP")
if [[ -z "$ZIP" || ! -f "$ZIP" ]]; then
post_activity "ERROR: download never appeared"; sleep 2; clear_activity
echo "ERROR: no zip in Downloads after 2 min — $PULL"; exit 3
fi
echo " download: $NEW"
# Step 7: Distribute Altium + Fusion files
post_activity "distributing Altium + Fusion"
EXTRACT=$(mktemp -d)
unzip -q -o "$ZIP" -d "$EXTRACT"
LIB="$CF/library/$MPN"
mkdir -p "$LIB"
ADDED=""
SCH=$(find "$EXTRACT" -name "*.SchLib" | head -1)
PCB=$(find "$EXTRACT" -name "*.PcbLib" | head -1)
LBR=$(find "$EXTRACT" -name "*.lbr" | head -1)
[[ -n "$SCH" ]] && cp "$SCH" "$LIB/$MPN.SchLib" && ADDED="$ADDED SchLib"
[[ -n "$PCB" ]] && cp "$PCB" "$LIB/$MPN.PcbLib" && ADDED="$ADDED PcbLib"
[[ -n "$LBR" ]] && cp "$LBR" "$LIB/$MPN.lbr" && ADDED="$ADDED lbr"
rm -rf "$EXTRACT"
echo " distributed:$ADDED"
post_activity "done"
sleep 1
clear_activity
echo "OK $MPN"