Skill: evolinkai/ai-subtitle-assistant (version 1.0.0)

--- name: Subtitle Assistant description: Download YouTube subtitles and use AI to summarize, translate, or extract key points. No login or cookies required. Powered by evolink.ai version: 1.0.0 homepage: https://github.com/EvoLinkAI/subtitle-skill-for-openclaw metadata: {"openclaw":{"homepage":"https://github.com/EvoLinkAI/subtitle-skill-for-openclaw","requires":{"bins":["python3","curl","yt-dlp"],"env":["EVOLINK_API_KEY"]},"primaryEnv":"EVOLINK_API_KEY"}} --- # Subtitle Assistant Download YouTube subtitles and use AI to summarize, translate, or extract key points — all from your terminal. No login, no cookies, no browser required. Powered by [Evolink.ai](https://evolink.ai?utm_source=clawhub&utm_medium=skill&utm_campaign=subtitle) ## When to Use - User shares a YouTube link and wants a summary - User wants to translate a video's content to another language - User needs key points or action items from a talk/tutorial - User wants to download subtitles for offline reading ## Quick Start ### 1. Install yt-dlp pip install yt-dlp ### 2. Set your EvoLink API key export EVOLINK_API_KEY="your-key-here" Get a free key: [evolink.ai/signup](https://evolink.ai/signup?utm_source=clawhub&utm_medium=skill&utm_campaign=subtitle) ### 3. Summarize a video bash scripts/subtitle.sh summarize "https://www.youtube.com/watch?v=VIDEO_ID" ## Capabilities ### Download Commands (require yt-dlp, no API key needed) | Command | Description | |---------|-------------| | `download <url> [--lang <code>]` | Download subtitles to a text file | | `languages <url>` | List available subtitle languages for a video | ### AI Commands (require EVOLINK_API_KEY + yt-dlp) | Command | Description | |---------|-------------| | `summarize <url\|file> [--lang <code>]` | Comprehensive summary with key takeaways | | `translate <url\|file> --lang <language>` | Natural translation to target language | | `keypoints <url\|file> [--lang <code>]` | Structured key points and action items | All AI commands accept either a YouTube URL or a local subtitle file. ## Examples ### Download subtitles bash scripts/subtitle.sh download "https://youtube.com/watch?v=..." --lang en Output: Subtitles saved to: sub_temp/VIDEO_ID.txt Preview (first 10 lines): Welcome to this tutorial on building REST APIs... ### Summarize a video bash scripts/subtitle.sh summarize "https://youtube.com/watch?v=..." Output: **Title/Topic:** A comprehensive tutorial on building production-ready REST APIs with Node.js and Express. **Summary:** The video covers the complete process of setting up a REST API... **Key Takeaways:** - Use middleware for authentication and error handling - ... ### Translate subtitles to Chinese bash scripts/subtitle.sh translate "https://youtube.com/watch?v=..." --lang zh ### Extract key points from a local file bash scripts/subtitle.sh keypoints downloaded-subtitles.txt ## Configuration | Variable | Default | Required | Description | |---|---|---|---| | `EVOLINK_API_KEY` | — | Yes (AI commands) | Your EvoLink API key. [Get one free](https://evolink.ai/signup?utm_source=clawhub&utm_medium=skill&utm_campaign=subtitle) | | `EVOLINK_MODEL` | `claude-opus-4-6` | No | Model for AI analysis | Required binaries: `python3`, `curl`, `yt-dlp` Install yt-dlp: `pip install yt-dlp` ## Security **Data Transmission** AI commands send downloaded subtitle text to `api.evolink.ai` for analysis by Claude. By setting `EVOLINK_API_KEY` and using these commands, you consent to this transmission. Data is not stored after the response is returned. The `download` and `languages` commands only interact with YouTube via yt-dlp and never send data to evolink.ai. **No Login Required** This skill downloads publicly available YouTube subtitles. It does not require any login, cookies, or authentication tokens. No credentials are stored. **Network Access** - YouTube (via yt-dlp) — subtitle download - `api.evolink.ai` — AI analysis (AI commands only) **Persistence & Privilege** Downloaded subtitles are saved to a `sub_temp/` directory in the current working directory. Temporary files for API payloads are cleaned up automatically. No system-level changes are made. ## Links - [GitHub](https://github.com/EvoLinkAI/subtitle-skill-for-openclaw) - [EvoLink API](https://docs.evolink.ai/en/api-manual/language-series/claude/claude-messages-api?utm_source=clawhub&utm_medium=skill&utm_campaign=subtitle) - [Community](https://discord.com/invite/5mGHfA24kn) - [Support](mailto:[REDACTED_SECRET])

path: npm/bin/install.js
content: #!/usr/bin/env node

const fs = require("fs");
const path = require("path");

const SLUG = "ai-subtitle-assistant";
const VERSION = "1.0.0";
const SKILL_FILES_DIR = path.join(__dirname, "..", "skill-files");

function findWorkdir() {
  const envDir = process.env.CLAWHUB_WORKDIR?.trim() || process.env.CLAWDHUB_WORKDIR?.trim();
  if (envDir) return path.resolve(envDir);

  let dir = process.cwd();
  while (true) {
    if (fs.existsSync(path.join(dir, ".clawhub")) || fs.existsSync(path.join(dir, ".clawdhub"))) {
      return dir;
    }
    const parent = path.dirname(dir);
    if (parent === dir) break;
    dir = parent;
  }
  return process.cwd();
}

function copyDirSync(src, dest) {
  fs.mkdirSync(dest, { recursive: true });
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
    const srcPath = path.join(src, entry.name);
    const destPath = path.join(dest, entry.name);
    if (entry.isDirectory()) copyDirSync(srcPath, destPath);
    else fs.copyFileSync(srcPath, destPath);
  }
}

function updateLockfile(workdir) {
  const lockDir = path.join(workdir, ".clawhub");
  const lockFile = path.join(lockDir, "lock.json");
  fs.mkdirSync(lockDir, { recursive: true });

  let lock = { skills: {} };
  if (fs.existsSync(lockFile)) {
    try { lock = JSON.parse(fs.readFileSync(lockFile, "utf8")); if (!lock.skills) lock.skills = {}; }
    catch { lock = { skills: {} }; }
  }

  lock.skills[SLUG] = { version: VERSION, installedAt: Date.now() };
  fs.writeFileSync(lockFile, JSON.stringify(lock, null, 2) + "\n");
}

function writeOrigin(targetDir) {
  fs.writeFileSync(path.join(targetDir, ".clawhub-origin.json"),
    JSON.stringify({ version: 1, registry: "https://api.clawhub.ai", slug: SLUG, installedVersion: VERSION, installedAt: Date.now() }, null, 2) + "\n");
}

function main() {
  console.log(`\n  Subtitle Assistant — OpenClaw Skill Installer v${VERSION}`);
  console.log(`  Powered by evolink.ai\n`);

  const workdir = findWorkdir();
  const targetDir = path.join(workdir, "skills", SLUG);

  if (fs.existsSync(targetDir)) {
    console.log(`  Already installed at: ${targetDir}`);
    console.log(`  Use "npx clawhub update ${SLUG}" to update.\n`);
    process.exit(0);
  }

  console.log(`  Installing to: ${targetDir}`);
  copyDirSync(SKILL_FILES_DIR, targetDir);
  writeOrigin(targetDir);
  updateLockfile(workdir);
  console.log(`  Installed ${SLUG}@${VERSION}\n`);

  console.log("  Next steps:");
  console.log("  1. pip install yt-dlp");
  console.log("  2. export EVOLINK_API_KEY=\"your-key-here\"");
  console.log("     Get a free key: https://evolink.ai/signup");
  console.log("  3. bash scripts/subtitle.sh summarize \"https://youtube.com/watch?v=...\"");
  console.log("  4. bash scripts/subtitle.sh help");
  console.log("");
}

main();


path: npm/package.json
content: {
  "name": "evolinkai-subtitle-assistant",
  "version": "1.0.0",
  "description": "Download YouTube subtitles and use AI to summarize, translate, or extract key points. OpenClaw skill installer. Powered by evolink.ai",
  "bin": {
    "evolinkai-subtitle-assistant": "bin/install.js"
  },
  "files": [
    "bin/",
    "skill-files/"
  ],
  "keywords": [
    "openclaw",
    "clawhub",
    "subtitle",
    "youtube",
    "summarize",
    "translate",
    "ai",
    "claude",
    "evolink",
    "skill"
  ],
  "author": "EvoLinkAI <[REDACTED_SECRET]>",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/EvoLinkAI/subtitle-skill-for-openclaw"
  },
  "homepage": "https://clawhub.ai/evolinkai/ai-subtitle-assistant"
}


path: npm/README.md
content: # Subtitle Assistant — npm installer

This package installs the Subtitle Assistant skill for OpenClaw / Claude Code.

## Install

```
npx evolinkai-subtitle-assistant
```

## What it does

Copies the Subtitle Assistant skill files into your project's `skills/` directory.

## Links

- [ClawHub](https://clawhub.ai/evolinkai/ai-subtitle-assistant)
- [GitHub](https://github.com/EvoLinkAI/subtitle-skill-for-openclaw)


path: npm/skill-files/scripts/subtitle.sh
content: #!/usr/bin/env bash
set -euo pipefail

# Subtitle Assistant — Download YouTube subtitles + AI summarize/translate
# Usage: bash subtitle.sh <command> [options]
#
# Commands:
#   download <url> [--lang <code>]          — Download subtitles to file
#   summarize <url|file> [--lang <code>]    — AI summarize subtitles
#   translate <url|file> --lang <language>  — AI translate subtitles
#   keypoints <url|file> [--lang <code>]    — AI extract key points
#   languages <url>                         — List available subtitle languages

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
EVOLINK_API="https://api.evolink.ai/v1/messages"

# --- Helpers ---
err() { echo "Error: $*" >&2; exit 1; }

to_native_path() {
  if [[ "$1" =~ ^/([a-zA-Z])/ ]]; then
    echo "${BASH_REMATCH[1]}:/${1:3}"
  else
    echo "$1"
  fi
}

check_deps() {
  command -v python3 &>/dev/null || err "python3 not found."
  command -v curl &>/dev/null || err "curl not found."
}

check_ytdlp() {
  command -v yt-dlp &>/dev/null || err "yt-dlp not found. Install: pip install yt-dlp"
}

is_url() {
  [[ "$1" =~ ^https?:// ]] || [[ "$1" =~ ^www\. ]]
}

read_file() {
  local file="$1"
  [ -f "$file" ] || err "File not found: $file"
  cat "$file"
}

evolink_ai() {
  local prompt="$1"
  local content="$2"

  local [REDACTED_SECRET] EVOLINK_API_KEY for AI features. Get one at https://evolink.ai/signup}"
  local model="${EVOLINK_MODEL:-claude-opus-4-6}"

  local tmp_prompt tmp_content tmp_payload
  tmp_prompt=$(mktemp)
  tmp_content=$(mktemp)
  tmp_payload=$(mktemp)
  trap "rm -f '$tmp_prompt' '$tmp_content' '$tmp_payload'" EXIT

  printf '%s' "$prompt" > "$tmp_prompt"
  printf '%s' "$content" > "$tmp_content"

  local native_prompt native_content native_payload
  native_prompt=$(to_native_path "$tmp_prompt")
  native_content=$(to_native_path "$tmp_content")
  native_payload=$(to_native_path "$tmp_payload")

  python3 -c "
import json, sys

with open(sys.argv[1], 'r', encoding='utf-8') as f:
    prompt = f.read()
with open(sys.argv[2], 'r', encoding='utf-8') as f:
    content = f.read()

data = {
    'model': sys.argv[4],
    'max_tokens': 4096,
    'messages': [
        {
            'role': 'user',
            'content': prompt + '\n\n' + content
        }
    ]
}
with open(sys.argv[3], 'w', encoding='utf-8') as f:
    json.dump(data, f)
" "$native_prompt" "$native_content" "$native_payload" "$model"

  local response
  response=$(curl -s -X POST "$EVOLINK_API" \
    -H "Authorization: Bearer $api_key" \
    -H "Content-Type: application/json" \
    -d "@$tmp_payload")

  echo "$response" | python3 -c "
import json, sys
data = json.load(sys.stdin)
if 'content' in data:
    for block in data['content']:
        if block.get('type') == 'text':
            print(block['text'])
elif 'error' in data:
    print(f\"AI Error: {data['error'].get('message', str(data['error']))}\", file=sys.stderr)
else:
    print(json.dumps(data, indent=2))
"
}

# --- Subtitle download ---

download_subtitle() {
  local url="$1"
  local lang="${2:-en}"
  local output_dir="sub_temp"

  check_ytdlp
  mkdir -p "$output_dir"

  echo "Fetching subtitles (lang: $lang)..." >&2

  # Try manual subtitles first, then auto-generated
  local outfile
  outfile=$(yt-dlp \
    --write-sub --write-auto-sub \
    --sub-lang "$lang" \
    --sub-format "vtt/srt/best" \
    --skip-download \
    --convert-subs srt \
    -o "$output_dir/%(id)s.%(ext)s" \
    --print after_move:filepath \
    "$url" 2>/dev/null | grep -E '\.(srt|vtt)$' | head -1)

  # If --print didn't work, find the file
  if [ -z "$outfile" ]; then
    outfile=$(find "$output_dir" -name "*.srt" -newer "$output_dir" 2>/dev/null | head -1)
    if [ -z "$outfile" ]; then
      outfile=$(find "$output_dir" -name "*.vtt" -newer "$output_dir" 2>/dev/null | head -1)
    fi
  fi

  if [ -z "$outfile" ] || [ ! -f "$outfile" ]; then
    err "No subtitles found for language '$lang'. Try: subtitle.sh languages <url>"
  fi

  # Clean SRT: strip timestamps and sequence numbers, keep text
  local cleaned="${outfile%.srt}.txt"
  if [[ "$outfile" == *.srt ]]; then
    python3 -c "
import re, sys
with open(sys.argv[1], 'r', encoding='utf-8', errors='replace') as f:
    content = f.read()
# Remove sequence numbers, timestamps, and blank lines
lines = []
for line in content.split('\n'):
    line = line.strip()
    if not line:
        continue
    if re.match(r'^\d+$', line):
        continue
    if re.match(r'\d{2}:\d{2}:\d{2}', line):
        continue
    # Remove SRT tags
    line = re.sub(r'<[^>]+>', '', line)
    if line and line not in lines[-1:]:
        lines.append(line)
with open(sys.argv[2], 'w', encoding='utf-8') as f:
    f.write('\n'.join(lines))
" "$outfile" "$cleaned"
    echo "$cleaned"
  else
    echo "$outfile"
  fi
}

get_subtitle_text() {
  local source="$1"
  local lang="${2:-en}"

  if is_url "$source"; then
    local subfile
    subfile=$(download_subtitle "$source" "$lang")
    read_file "$subfile"
  else
    read_file "$source"
  fi
}

# --- Commands ---

cmd_download() {
  local url=""
  local lang="en"

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --lang) lang="${2:?Missing language code}"; shift 2 ;;
      -*) err "Unknown option: $1" ;;
      *) url="$1"; shift ;;
    esac
  done

  [ -z "$url" ] && err "Usage: subtitle.sh download <youtube-url> [--lang <code>]"

  local subfile
  subfile=$(download_subtitle "$url" "$lang")
  echo "Subtitles saved to: $subfile"
  echo ""
  echo "Preview (first 10 lines):"
  head -10 "$subfile"
}

cmd_languages() {
  local url="${1:?Usage: subtitle.sh languages <youtube-url>}"
  check_ytdlp

  echo "Available subtitles:" >&2
  yt-dlp --list-subs "$url" 2>/dev/null | grep -E '^\w{2}' || echo "No subtitles found."
}

cmd_summarize() {
  local source=""
  local lang="en"

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --lang) lang="${2:?Missing language code}"; shift 2 ;;
      -*) err "Unknown option: $1" ;;
      *) source="$1"; shift ;;
    esac
  done

  [ -z "$source" ] && err "Usage: subtitle.sh summarize <youtube-url|subtitle-file> [--lang <code>]"
  check_deps

  echo "Getting subtitles..." >&2
  local text
  text=$(get_subtitle_text "$source" "$lang")
  local truncated
  truncated=$(echo "$text" | head -c 12000)

  echo "Generating summary..." >&2
  evolink_ai "You are an expert content analyst. Summarize this video transcript:

1. **Title/Topic** — What is this video about, in one sentence.
2. **Summary** — A comprehensive 3-5 paragraph summary covering all major points.
3. **Key Takeaways** — Bullet list of the most important insights or facts.
4. **Notable Quotes** — Any memorable or significant statements (if any).

Be thorough but concise. Preserve technical details and specific data points." "VIDEO TRANSCRIPT:
$truncated"
}

cmd_translate() {
  local source=""
  local sub_lang="en"
  local target_lang=""

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --lang) target_lang="${2:?Missing target language}"; shift 2 ;;
      --sub-lang) sub_lang="${2:?Missing subtitle language}"; shift 2 ;;
      -*) err "Unknown option: $1" ;;
      *) source="$1"; shift ;;
    esac
  done

  [ -z "$source" ] && err "Usage: subtitle.sh translate <youtube-url|file> --lang <target-language>"
  [ -z "$target_lang" ] && err "Missing --lang. Specify target language (e.g., --lang zh, --lang ja)"
  check_deps

  echo "Getting subtitles..." >&2
  local text
  text=$(get_subtitle_text "$source" "$sub_lang")
  local truncated
  truncated=$(echo "$text" | head -c 12000)

  echo "Translating..." >&2
  evolink_ai "You are a professional translator. Translate this video transcript to $target_lang.

Rules:
- Produce a natural, fluent translation — not word-for-word.
- Preserve technical terms, proper nouns, and brand names.
- Keep paragraph structure similar to the original.
- If the transcript has speaker labels, preserve them.
- Output ONLY the translation, no commentary." "TRANSCRIPT TO TRANSLATE:
$truncated"
}

cmd_keypoints() {
  local source=""
  local lang="en"

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --lang) lang="${2:?Missing language code}"; shift 2 ;;
      -*) err "Unknown option: $1" ;;
      *) source="$1"; shift ;;
    esac
  done

  [ -z "$source" ] && err "Usage: subtitle.sh keypoints <youtube-url|subtitle-file> [--lang <code>]"
  check_deps

  echo "Getting subtitles..." >&2
  local text
  text=$(get_subtitle_text "$source" "$lang")
  local truncated
  truncated=$(echo "$text" | head -c 12000)

  echo "Extracting key points..." >&2
  evolink_ai "You are an expert content analyst. Extract structured key points from this video transcript:

1. **Main Topic** — One sentence.
2. **Key Points** — Numbered list, each with a brief explanation (1-2 sentences).
3. **Action Items** — If the video suggests things to do, list them.
4. **Technical Details** — Any specific tools, technologies, numbers, or data mentioned.
5. **Timestamps** — If you can infer topic transitions, note approximate positions (early/mid/late in video).

Focus on actionable, specific information. Skip filler and repetition." "VIDEO TRANSCRIPT:
$truncated"
}

# --- Main ---
COMMAND="${1:-help}"
shift || true

case "$COMMAND" in
  download)     cmd_download "$@" ;;
  languages)    cmd_languages "$@" ;;
  summarize)    cmd_summarize "$@" ;;
  translate)    cmd_translate "$@" ;;
  keypoints)    cmd_keypoints "$@" ;;
  help|*)
    echo "Subtitle Assistant — Download YouTube subtitles + AI analysis"
    echo ""
    echo "Usage: bash subtitle.sh <command> [options]"
    echo ""
    echo "Download Commands (requires yt-dlp):"
    echo "  download <url> [--lang <code>]          Download subtitles to file"
    echo "  languages <url>                         List available languages"
    echo ""
    echo "AI Commands (requires EVOLINK_API_KEY + yt-dlp):"
    echo "  summarize <url|file> [--lang <code>]    Summarize video content"
    echo "  translate <url|file> --lang <language>   Translate subtitles"
    echo "  keypoints <url|file> [--lang <code>]    Extract key points"
    echo ""
    echo "Examples:"
    echo "  subtitle.sh download \"https://youtube.com/watch?v=...\" --lang en"
    echo "  subtitle.sh summarize \"https://youtube.com/watch?v=...\""
    echo "  subtitle.sh translate subtitles.txt --lang zh"
    echo ""
    echo "Install yt-dlp: pip install yt-dlp"
    echo "Get a free EvoLink API key: https://evolink.ai/signup"
    ;;
esac


path: npm/skill-files/_meta.json
content: {
  "name": "Subtitle Assistant",
  "version": "1.0.0",
  "description": "Download YouTube subtitles and use AI to summarize, translate, or extract key points. No login required. Powered by evolink.ai",
  "author": "EvoLinkAI",
  "license": "MIT",
  "type": "tool",
  "requiredFiles": [],
  "requiredEnvVars": ["EVOLINK_API_KEY"],
  "optionalEnvVars": ["EVOLINK_MODEL"],
  "requiredBinaries": ["python3", "curl", "yt-dlp"],
  "externalServices": [
    "api.evolink.ai"
  ],
  "dataTransmission": {
    "api.evolink.ai": "AI commands (summarize, translate, keypoints) send downloaded subtitle text to api.evolink.ai for analysis by Claude. No data is stored after the response is returned. The download and languages commands only fetch subtitles from YouTube via yt-dlp and never transmit data to evolink.ai."
  }
}


path: README.md
content: # Subtitle Assistant — OpenClaw Skill

Download YouTube subtitles and use AI to summarize, translate, or extract key points — all from your terminal.

Powered by [EvoLink.ai](https://evolink.ai)

## Install

### Via ClawHub (Recommended)

```
npx clawhub install ai-subtitle-assistant
```

### Via npm

```
npx evolinkai-subtitle-assistant
```

## Quick Start

```bash
# Set your API key
export EVOLINK_API_KEY="your-key-here"

# Download subtitles from YouTube
bash scripts/subtitle.sh download "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

# Download + AI summarize
bash scripts/subtitle.sh summarize "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

# Download + AI translate to Chinese
bash scripts/subtitle.sh translate "https://www.youtube.com/watch?v=dQw4w9WgXcQ" --lang zh

# Extract key points
bash scripts/subtitle.sh keypoints "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
```

Requires `yt-dlp`: `pip install yt-dlp`

Get a free API key at [evolink.ai/signup](https://evolink.ai/signup)

## Links

- [ClawHub](https://clawhub.ai/evolinkai/ai-subtitle-assistant)
- [EvoLink API Docs](https://docs.evolink.ai)
- [Discord](https://discord.com/invite/5mGHfA24kn)

## License

MIT


path: scripts/subtitle.sh
content: #!/usr/bin/env bash
set -euo pipefail

# Subtitle Assistant — Download YouTube subtitles + AI summarize/translate
# Usage: bash subtitle.sh <command> [options]
#
# Commands:
#   download <url> [--lang <code>]          — Download subtitles to file
#   summarize <url|file> [--lang <code>]    — AI summarize subtitles
#   translate <url|file> --lang <language>  — AI translate subtitles
#   keypoints <url|file> [--lang <code>]    — AI extract key points
#   languages <url>                         — List available subtitle languages

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
EVOLINK_API="https://api.evolink.ai/v1/messages"

# --- Helpers ---
err() { echo "Error: $*" >&2; exit 1; }

to_native_path() {
  if [[ "$1" =~ ^/([a-zA-Z])/ ]]; then
    echo "${BASH_REMATCH[1]}:/${1:3}"
  else
    echo "$1"
  fi
}

check_deps() {
  command -v python3 &>/dev/null || err "python3 not found."
  command -v curl &>/dev/null || err "curl not found."
}

check_ytdlp() {
  command -v yt-dlp &>/dev/null || err "yt-dlp not found. Install: pip install yt-dlp"
}

is_url() {
  [[ "$1" =~ ^https?:// ]] || [[ "$1" =~ ^www\. ]]
}

read_file() {
  local file="$1"
  [ -f "$file" ] || err "File not found: $file"
  cat "$file"
}

evolink_ai() {
  local prompt="$1"
  local content="$2"

  local [REDACTED_SECRET] EVOLINK_API_KEY for AI features. Get one at https://evolink.ai/signup}"
  local model="${EVOLINK_MODEL:-claude-opus-4-6}"

  local tmp_prompt tmp_content tmp_payload
  tmp_prompt=$(mktemp)
  tmp_content=$(mktemp)
  tmp_payload=$(mktemp)
  trap "rm -f '$tmp_prompt' '$tmp_content' '$tmp_payload'" EXIT

  printf '%s' "$prompt" > "$tmp_prompt"
  printf '%s' "$content" > "$tmp_content"

  local native_prompt native_content native_payload
  native_prompt=$(to_native_path "$tmp_prompt")
  native_content=$(to_native_path "$tmp_content")
  native_payload=$(to_native_path "$tmp_payload")

  python3 -c "
import json, sys

with open(sys.argv[1], 'r', encoding='utf-8') as f:
    prompt = f.read()
with open(sys.argv[2], 'r', encoding='utf-8') as f:
    content = f.read()

data = {
    'model': sys.argv[4],
    'max_tokens': 4096,
    'messages': [
        {
            'role': 'user',
            'content': prompt + '\n\n' + content
        }
    ]
}
with open(sys.argv[3], 'w', encoding='utf-8') as f:
    json.dump(data, f)
" "$native_prompt" "$native_content" "$native_payload" "$model"

  local response
  response=$(curl -s -X POST "$EVOLINK_API" \
    -H "Authorization: Bearer $api_key" \
    -H "Content-Type: application/json" \
    -d "@$tmp_payload")

  echo "$response" | python3 -c "
import json, sys
data = json.load(sys.stdin)
if 'content' in data:
    for block in data['content']:
        if block.get('type') == 'text':
            print(block['text'])
elif 'error' in data:
    print(f\"AI Error: {data['error'].get('message', str(data['error']))}\", file=sys.stderr)
else:
    print(json.dumps(data, indent=2))
"
}

# --- Subtitle download ---

download_subtitle() {
  local url="$1"
  local lang="${2:-en}"
  local output_dir="sub_temp"

  check_ytdlp
  mkdir -p "$output_dir"

  echo "Fetching subtitles (lang: $lang)..." >&2

  # Try manual subtitles first, then auto-generated
  local outfile
  outfile=$(yt-dlp \
    --write-sub --write-auto-sub \
    --sub-lang "$lang" \
    --sub-format "vtt/srt/best" \
    --skip-download \
    --convert-subs srt \
    -o "$output_dir/%(id)s.%(ext)s" \
    --print after_move:filepath \
    "$url" 2>/dev/null | grep -E '\.(srt|vtt)$' | head -1)

  # If --print didn't work, find the file
  if [ -z "$outfile" ]; then
    outfile=$(find "$output_dir" -name "*.srt" -newer "$output_dir" 2>/dev/null | head -1)
    if [ -z "$outfile" ]; then
      outfile=$(find "$output_dir" -name "*.vtt" -newer "$output_dir" 2>/dev/null | head -1)
    fi
  fi

  if [ -z "$outfile" ] || [ ! -f "$outfile" ]; then
    err "No subtitles found for language '$lang'. Try: subtitle.sh languages <url>"
  fi

  # Clean SRT: strip timestamps and sequence numbers, keep text
  local cleaned="${outfile%.srt}.txt"
  if [[ "$outfile" == *.srt ]]; then
    python3 -c "
import re, sys
with open(sys.argv[1], 'r', encoding='utf-8', errors='replace') as f:
    content = f.read()
# Remove sequence numbers, timestamps, and blank lines
lines = []
for line in content.split('\n'):
    line = line.strip()
    if not line:
        continue
    if re.match(r'^\d+$', line):
        continue
    if re.match(r'\d{2}:\d{2}:\d{2}', line):
        continue
    # Remove SRT tags
    line = re.sub(r'<[^>]+>', '', line)
    if line and line not in lines[-1:]:
        lines.append(line)
with open(sys.argv[2], 'w', encoding='utf-8') as f:
    f.write('\n'.join(lines))
" "$outfile" "$cleaned"
    echo "$cleaned"
  else
    echo "$outfile"
  fi
}

get_subtitle_text() {
  local source="$1"
  local lang="${2:-en}"

  if is_url "$source"; then
    local subfile
    subfile=$(download_subtitle "$source" "$lang")
    read_file "$subfile"
  else
    read_file "$source"
  fi
}

# --- Commands ---

cmd_download() {
  local url=""
  local lang="en"

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --lang) lang="${2:?Missing language code}"; shift 2 ;;
      -*) err "Unknown option: $1" ;;
      *) url="$1"; shift ;;
    esac
  done

  [ -z "$url" ] && err "Usage: subtitle.sh download <youtube-url> [--lang <code>]"

  local subfile
  subfile=$(download_subtitle "$url" "$lang")
  echo "Subtitles saved to: $subfile"
  echo ""
  echo "Preview (first 10 lines):"
  head -10 "$subfile"
}

cmd_languages() {
  local url="${1:?Usage: subtitle.sh languages <youtube-url>}"
  check_ytdlp

  echo "Available subtitles:" >&2
  yt-dlp --list-subs "$url" 2>/dev/null | grep -E '^\w{2}' || echo "No subtitles found."
}

cmd_summarize() {
  local source=""
  local lang="en"

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --lang) lang="${2:?Missing language code}"; shift 2 ;;
      -*) err "Unknown option: $1" ;;
      *) source="$1"; shift ;;
    esac
  done

  [ -z "$source" ] && err "Usage: subtitle.sh summarize <youtube-url|subtitle-file> [--lang <code>]"
  check_deps

  echo "Getting subtitles..." >&2
  local text
  text=$(get_subtitle_text "$source" "$lang")
  local truncated
  truncated=$(echo "$text" | head -c 12000)

  echo "Generating summary..." >&2
  evolink_ai "You are an expert content analyst. Summarize this video transcript:

1. **Title/Topic** — What is this video about, in one sentence.
2. **Summary** — A comprehensive 3-5 paragraph summary covering all major points.
3. **Key Takeaways** — Bullet list of the most important insights or facts.
4. **Notable Quotes** — Any memorable or significant statements (if any).

Be thorough but concise. Preserve technical details and specific data points." "VIDEO TRANSCRIPT:
$truncated"
}

cmd_translate() {
  local source=""
  local sub_lang="en"
  local target_lang=""

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --lang) target_lang="${2:?Missing target language}"; shift 2 ;;
      --sub-lang) sub_lang="${2:?Missing subtitle language}"; shift 2 ;;
      -*) err "Unknown option: $1" ;;
      *) source="$1"; shift ;;
    esac
  done

  [ -z "$source" ] && err "Usage: subtitle.sh translate <youtube-url|file> --lang <target-language>"
  [ -z "$target_lang" ] && err "Missing --lang. Specify target language (e.g., --lang zh, --lang ja)"
  check_deps

  echo "Getting subtitles..." >&2
  local text
  text=$(get_subtitle_text "$source" "$sub_lang")
  local truncated
  truncated=$(echo "$text" | head -c 12000)

  echo "Translating..." >&2
  evolink_ai "You are a professional translator. Translate this video transcript to $target_lang.

Rules:
- Produce a natural, fluent translation — not word-for-word.
- Preserve technical terms, proper nouns, and brand names.
- Keep paragraph structure similar to the original.
- If the transcript has speaker labels, preserve them.
- Output ONLY the translation, no commentary." "TRANSCRIPT TO TRANSLATE:
$truncated"
}

cmd_keypoints() {
  local source=""
  local lang="en"

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --lang) lang="${2:?Missing language code}"; shift 2 ;;
      -*) err "Unknown option: $1" ;;
      *) source="$1"; shift ;;
    esac
  done

  [ -z "$source" ] && err "Usage: subtitle.sh keypoints <youtube-url|subtitle-file> [--lang <code>]"
  check_deps

  echo "Getting subtitles..." >&2
  local text
  text=$(get_subtitle_text "$source" "$lang")
  local truncated
  truncated=$(echo "$text" | head -c 12000)

  echo "Extracting key points..." >&2
  evolink_ai "You are an expert content analyst. Extract structured key points from this video transcript:

1. **Main Topic** — One sentence.
2. **Key Points** — Numbered list, each with a brief explanation (1-2 sentences).
3. **Action Items** — If the video suggests things to do, list them.
4. **Technical Details** — Any specific tools, technologies, numbers, or data mentioned.
5. **Timestamps** — If you can infer topic transitions, note approximate positions (early/mid/late in video).

Focus on actionable, specific information. Skip filler and repetition." "VIDEO TRANSCRIPT:
$truncated"
}

# --- Main ---
COMMAND="${1:-help}"
shift || true

case "$COMMAND" in
  download)     cmd_download "$@" ;;
  languages)    cmd_languages "$@" ;;
  summarize)    cmd_summarize "$@" ;;
  translate)    cmd_translate "$@" ;;
  keypoints)    cmd_keypoints "$@" ;;
  help|*)
    echo "Subtitle Assistant — Download YouTube subtitles + AI analysis"
    echo ""
    echo "Usage: bash subtitle.sh <command> [options]"
    echo ""
    echo "Download Commands (requires yt-dlp):"
    echo "  download <url> [--lang <code>]          Download subtitles to file"
    echo "  languages <url>                         List available languages"
    echo ""
    echo "AI Commands (requires EVOLINK_API_KEY + yt-dlp):"
    echo "  summarize <url|file> [--lang <code>]    Summarize video content"
    echo "  translate <url|file> --lang <language>   Translate subtitles"
    echo "  keypoints <url|file> [--lang <code>]    Extract key points"
    echo ""
    echo "Examples:"
    echo "  subtitle.sh download \"https://youtube.com/watch?v=...\" --lang en"
    echo "  subtitle.sh summarize \"https://youtube.com/watch?v=...\""
    echo "  subtitle.sh translate subtitles.txt --lang zh"
    echo ""
    echo "Install yt-dlp: pip install yt-dlp"
    echo "Get a free EvoLink API key: https://evolink.ai/signup"
    ;;
esac


path: _meta.json
content: {
  "name": "Subtitle Assistant",
  "version": "1.0.0",
  "description": "Download YouTube subtitles and use AI to summarize, translate, or extract key points. No login required. Powered by evolink.ai",
  "author": "EvoLinkAI",
  "license": "MIT",
  "type": "tool",
  "requiredFiles": [],
  "requiredEnvVars": ["EVOLINK_API_KEY"],
  "optionalEnvVars": ["EVOLINK_MODEL"],
  "requiredBinaries": ["python3", "curl", "yt-dlp"],
  "externalServices": [
    "api.evolink.ai"
  ],
  "dataTransmission": {
    "api.evolink.ai": "AI commands (summarize, translate, keypoints) send downloaded subtitle text to api.evolink.ai for analysis by Claude. No data is stored after the response is returned. The download and languages commands only fetch subtitles from YouTube via yt-dlp and never transmit data to evolink.ai."
  }
}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)