Skip to main content
ClaudeWave
Skill128 repo starsupdated 2mo ago

torrent-search

The torrent-search skill queries Torznab-compatible indexers to find torrents by title or IMDB ID, returning magnet links along with metadata like file size, seeders, resolution, and codec. Use this when a user requests torrent downloads for movies or TV shows, provides an IMDB identifier for content lookup, or needs to compare available quality options across releases.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/besoeasy/open-skills /tmp/torrent-search && cp -r /tmp/torrent-search/skills/torrent-search ~/.claude/skills/torrent-search
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Torrent Search

Search any Torznab-compatible indexer (e.g. bitmagnet) for torrents by title or IMDB ID. Returns magnet links, file sizes, seeders, resolution, and codec.

## When to use

- User asks to find a torrent for a movie, TV show, or any other content
- User provides an IMDB ID (e.g. `tt1234567`) and wants download options
- You need to programmatically retrieve a magnet link for a given title
- User asks to compare available qualities (720p, 1080p, 2160p) for a release

## Required tools / APIs

- `curl` — HTTP requests (pre-installed on most systems)
- `jq` — JSON parsing (used after XML→JSON conversion)
- `xmllint` — XML parsing (optional, from `libxml2-utils`)
- A running Torznab endpoint — examples use `https://bitmagnetfortheweebs.midnightignite.me/torznab/api`

Install options:

```bash
# Ubuntu/Debian
sudo apt-get install -y curl jq libxml2-utils

# macOS
brew install curl jq libxml2

# Node.js (no extra packages — uses native fetch + DOMParser via fast-xml-parser)
npm install fast-xml-parser
```

## Skills

### search_by_title

Search for torrents using a free-text title query.

```bash
TORZNAB_URL="https://bitmagnetfortheweebs.midnightignite.me/torznab/api"
QUERY="Breaking Bad"

curl -fsS --max-time 15 \
  "${TORZNAB_URL}?t=search&q=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$QUERY")" \
  | xmllint --xpath "//item" - 2>/dev/null \
  | grep -oP '(?<=<title>).*?(?=</title>)'
```

Full extraction with magnet links:

```bash
TORZNAB_URL="https://bitmagnetfortheweebs.midnightignite.me/torznab/api"
QUERY="Inception 2010"

xml=$(curl -fsS --max-time 15 \
  "${TORZNAB_URL}?t=search&q=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$QUERY")")

# Print title + magnet for each result
echo "$xml" | python3 - << 'EOF'
import sys, xml.etree.ElementTree as ET

data = sys.stdin.read()
root = ET.fromstring(data)
ns = {'torznab': 'http://torznab.com/schemas/2015/feed'}

for item in root.findall('.//item'):
    title = item.findtext('title', '')
    size  = item.findtext('size', '0')
    enc   = item.find('enclosure')
    magnet = enc.get('url') if enc is not None else ''

    attrs = {a.get('name'): a.get('value') for a in item.findall('torznab:attr', ns)}
    seeders    = attrs.get('seeders', '?')
    resolution = attrs.get('resolution', '')
    codec      = attrs.get('video', '')

    size_gb = round(int(size) / 1_073_741_824, 2)
    print(f"{title}")
    print(f"  Size: {size_gb} GB  Seeders: {seeders}  {resolution} {codec}")
    print(f"  Magnet: {magnet[:80]}...")
    print()
EOF
```

**Node.js:**

```javascript
import { XMLParser } from 'fast-xml-parser';

const TORZNAB_URL = 'https://bitmagnetfortheweebs.midnightignite.me/torznab/api';

async function searchTorrents(query) {
  const url = `${TORZNAB_URL}?t=search&q=${encodeURIComponent(query)}`;
  const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  const xml = await res.text();
  const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' });
  const doc = parser.parse(xml);

  const items = doc?.rss?.channel?.item ?? [];
  const list = Array.isArray(items) ? items : [items];

  return list.map(item => {
    const attrs = {};
    const rawAttrs = item['torznab:attr'] ?? [];
    const attrList = Array.isArray(rawAttrs) ? rawAttrs : [rawAttrs];
    for (const a of attrList) attrs[a['@_name']] = a['@_value'];

    return {
      title:      item.title,
      sizeBytes:  Number(item.size ?? 0),
      sizeGB:     +(Number(item.size ?? 0) / 1_073_741_824).toFixed(2),
      magnet:     item.enclosure?.['@_url'] ?? attrs.magneturl ?? '',
      infohash:   attrs.infohash ?? '',
      seeders:    Number(attrs.seeders ?? 0),
      leechers:   Number(attrs.leechers ?? 0),
      resolution: attrs.resolution ?? '',
      codec:      attrs.video ?? '',
      year:       attrs.year ?? '',
      imdb:       attrs.imdb ? `tt${attrs.imdb}` : '',
    };
  });
}

// Usage
searchTorrents('Inception 2010').then(results => {
  results.forEach(r => {
    console.log(`${r.title}`);
    console.log(`  ${r.sizeGB} GB | ${r.resolution} ${r.codec} | ${r.seeders} seeders`);
    console.log(`  ${r.magnet.slice(0, 80)}...`);
    console.log();
  });
});
```

---

### search_by_imdb_id

Search by exact IMDB ID to get all available releases for a specific title.

```bash
TORZNAB_URL="https://bitmagnetfortheweebs.midnightignite.me/torznab/api"
IMDB_ID="tt12735488"   # Kalki 2898 AD

curl -fsS --max-time 15 \
  "${TORZNAB_URL}?t=search&q=${IMDB_ID}" \
  | python3 - << 'EOF'
import sys, xml.etree.ElementTree as ET

root = ET.fromstring(sys.stdin.read())
ns = {'torznab': 'http://torznab.com/schemas/2015/feed'}

for item in root.findall('.//item'):
    title = item.findtext('title', '')
    size  = int(item.findtext('size', '0'))
    enc   = item.find('enclosure')
    magnet = enc.get('url') if enc is not None else ''
    attrs = {a.get('name'): a.get('value') for a in item.findall('torznab:attr', ns)}

    print(f"[{attrs.get('resolution','?'):6}] {round(size/1e9,1):5.1f}GB  "
          f"S:{attrs.get('seeders','?'):>3}  {title}")
EOF
```

**Node.js:**

```javascript
import { XMLParser } from 'fast-xml-parser';

const TORZNAB_URL = 'https://bitmagnetfortheweebs.midnightignite.me/torznab/api';

async function searchByImdb(imdbId) {
  // imdbId format: 'tt1234567' or just '1234567'
  const id = imdbId.startsWith('tt') ? imdbId : `tt${imdbId}`;
  const url = `${TORZNAB_URL}?t=search&q=${id}`;

  const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  const xml = await res.text();
  const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' });
  const doc = parser.parse(xml);

  const items = doc?.rss?.channel?.item ?? [];
  const list = Array.isArray(items) ? items : [items];

  return list.map(item => {
    con