yousufjoyian

YouTube Transcripts - Project Skill

- **Toggle A2UI panel off/on**: Forces WebSocket reconnect if sidecar was restarted

yousufjoyian 0 Updated 4mo ago
GitHub

Install

npx skillscat add yousufjoyian/claude-skills/youtube-transcripts

Install via the SkillsCat registry.

SKILL.md

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.log

Startup 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.txt

Step 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 grep

If 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_EOF

IMPORTANT: Always append (>>) never truncate. Include <!-- ts:... --> for cache-bust (sidecar deduplicates by MD5).

Step 5: Verify and confirm

tail -5 /tmp/sidecar_${SIDECAR_PORT}.log

Should 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/null

Key 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.log for 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