- **Toggle A2UI panel off/on**: Forces WebSocket reconnect if sidecar was restarted
Install
npx skillscat add yousufjoyian/claude-skills/youtube-transcripts Install via the SkillsCat registry.
YouTube Transcripts - Project Skill
Triggers
- "youtube-transcripts", "transcript app", "start youtube-transcripts"
- "open youtube-transcripts", "show transcript app"
What This Skill Does
Starts the YouTube Transcripts Flask app, ensures the A2UI sidecar is alive, and embeds the app in the A2UI panel with a refresh button toolbar.
Features
- Single video transcript fetch with export (txt, srt, vtt, json)
- Channel search — enter channel name/@handle, load all videos, select and batch fetch
- Job-based background processing — polls every 2s, shows progress bar, incremental results
- Auto-download toggle — each transcript downloads to device as soon as it completes
- Webshare Residential proxy — automatic IP rotation via
WebshareProxyConfig
LAUNCH PROCEDURE (Execute All Steps - No Questions)
Step 1: Start the server
# Kill any old instance
pkill -f "python.*app.py" 2>/dev/null
sleep 1
# Start fresh
cd /home/yousuf/local_workspaces/youtube-transcripts
source venv/bin/activate
nohup python app.py > /tmp/youtube-transcripts.log 2>&1 &
sleep 3
# Verify
curl -s http://localhost:3003/api/health
head -3 /tmp/youtube-transcripts.logStartup log should show: Proxy configured: Webshare Residential (qrwsyzgr)
If venv doesn't exist:
python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txtStep 2: Find the correct A2UI terminal
Critical: Terminal IDs change when TriClaude restarts. Always look up the current one.
# Get current terminal config from projects.json
# Find the terminal that matches your project (look for children-trivia or whichever project this session is under)
cat /home/yousuf/GoogleDrive/PROJECTS/.triclaude/projects.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for p in data.get('projects', []):
for t in p.get('terminals', []):
print(f\"{p['name']}: {t['id']} sidecarPort={t['sidecarPort']}\")
"Set variables for your terminal:
TERM_ID="<terminal_id>"
SIDECAR_PORT=<sidecarPort>
A2UI_LOG="/home/yousuf/GoogleDrive/PROJECTS/.triclaude/runtime/terminals/${TERM_ID}/a2ui_input.log"Step 3: Ensure A2UI sidecar is alive and watching the RIGHT file
# Check if sidecar is running on the correct port AND watching the correct file
ps aux | grep "a2ui_sidecar.*${SIDECAR_PORT}" | grep -v grepIf NOT running, or watching a stale terminal log:
# Kill stale sidecar on this port
fuser -k ${SIDECAR_PORT}/tcp 2>/dev/null
sleep 1
# Start fresh sidecar (use youtube-transcripts venv — has websockets)
source /home/yousuf/local_workspaces/youtube-transcripts/venv/bin/activate
nohup python3 /home/yousuf/GoogleDrive/PROJECTS/APPS/TriClaude/scripts/a2ui_sidecar.py \
--port ${SIDECAR_PORT} \
--log "${A2UI_LOG}" \
> /tmp/sidecar_${SIDECAR_PORT}.log 2>&1 &
sleep 2
# Verify listening
ss -tlnp | grep ${SIDECAR_PORT}Step 4: Get Tailscale IP and embed in A2UI
User is on Android. Always use Tailscale IP. Never localhost or LAN IP.
TAILSCALE_IP=$(ip -4 addr show tailscale0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
TS=$(date +%s%N)
cat << A2UI_EOF >> "$A2UI_LOG"
<!-- A2UI:START -->
<!-- ts:${TS} -->
<!DOCTYPE html>
<html>
<head>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { background:#0f172a; height:100vh; display:flex; flex-direction:column; }
.toolbar {
display:flex; align-items:center; justify-content:space-between;
padding:6px 12px; background:#1e293b; border-bottom:1px solid #334155;
}
.toolbar-title { color:#94a3b8; font-family:system-ui; font-size:12px; font-weight:600; letter-spacing:0.5px; text-transform:uppercase; }
.refresh-btn {
background:#334155; color:#e2e8f0; border:1px solid #475569; border-radius:6px;
padding:4px 12px; font-size:12px; font-family:system-ui; cursor:pointer;
display:flex; align-items:center; gap:5px; transition: background 0.15s;
}
.refresh-btn:hover { background:#475569; }
.refresh-btn:active { background:#3b82f6; }
.refresh-btn svg { width:14px; height:14px; }
.refresh-btn.spinning svg { animation: spin 0.6s linear; }
@keyframes spin { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }
iframe { flex:1; border:none; width:100%; }
</style>
</head>
<body>
<div class="toolbar">
<span class="toolbar-title">YouTube Transcripts</span>
<button class="refresh-btn" onclick="refreshApp(this)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="23 4 23 10 17 10"/>
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
</svg>
Refresh
</button>
</div>
<iframe id="yt-frame" src="http://${TAILSCALE_IP}:3003" sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-downloads"></iframe>
<script>
function refreshApp(btn) {
btn.classList.add('spinning');
var f = document.getElementById('yt-frame');
f.src = 'http://${TAILSCALE_IP}:3003?' + Date.now();
setTimeout(function(){ btn.classList.remove('spinning'); }, 700);
}
</script>
</body>
</html>
<!-- A2UI:END -->
A2UI_EOFIMPORTANT: Always append (>>) never truncate. Include <!-- ts:... --> for cache-bust (sidecar deduplicates by MD5).
Step 5: Verify and confirm
tail -5 /tmp/sidecar_${SIDECAR_PORT}.logShould show "A2UI block started" and "A2UI block complete".
Tell the user:
YouTube Transcript Fetcher is live.
- A2UI panel: embedded (with refresh button)
- Direct URL: http://<TAILSCALE_IP>:3003
- Auto-download toggle available next to Fetch button
- Proxy: Webshare Residential (auto-rotating)STOP
pkill -f "python.*app.py" 2>/dev/nullKey Facts
| Setting | Value |
|---|---|
| Project Path | /home/yousuf/local_workspaces/youtube-transcripts |
| Port | 3003 |
| Config | .env (YOUTUBE_API_KEY, FLASK_PORT, WEBSHARE_USER, WEBSHARE_PASS) |
| Proxy | Webshare Residential via WebshareProxyConfig (auto-rotation) |
| Logs | /tmp/youtube-transcripts.log |
| Access | Tailscale IP only (user is on Android) |
| Sidecar script | /home/yousuf/GoogleDrive/PROJECTS/APPS/TriClaude/scripts/a2ui_sidecar.py |
| Sidecar needs websockets | Use youtube-transcripts venv to launch it |
| Never truncate a2ui_input.log | Truncating breaks the sidecar file descriptor |
| Terminal IDs change on restart | Always look up current terminal from projects.json |
Troubleshooting
- Panel blank: Sidecar dead or watching wrong terminal. Check
ps aux | grep sidecar, verify log path matches current terminal ID from projects.json - Transcripts failing: Check
/tmp/youtube-transcripts.logfor proxy errors. Startup should say "Webshare Residential" - Embed not updating: Sidecar deduplicates by MD5. Ensure
<!-- ts:... -->timestamp differs each time - Toggle A2UI panel off/on: Forces WebSocket reconnect if sidecar was restarted