bilibili-hub · git:20260801.3cb01ce · 2026-08-01 · sha256 ff17d31dbfbaf4e2

bilibili-hub git:20260801.3cb01ceA

Immutable. This exact content is served forever at /api/v1/blob/ff17d31dbfbaf4e2.

---
name: bilibili-hub
description: >
  A skill for reading and writing Bilibili data with Python + UV, using bilibili-api-python + aiohttp.
  It automatically obtains cookies through `browser_use get_cookies` for authentication, so no manual copying is required.
  Supports video details, subtitles, AI summaries, comments, user profiles, search, popular videos/rankings, following dynamics feed,
  favorites, Watch Later, viewing history, interactions (likes, coins, triple action), publishing and deleting dynamics, and more.
  When a user mentions "Bilibili," "bilibili," "Bilibili videos," "Bilibili popular," "bilibili-hub,"
  "get Bilibili data," "Bilibili subtitles," "Bilibili comments," "Bilibili favorites," "Bilibili dynamics,"
  or any scenario that requires programmatically reading or writing Bilibili content, this skill must be triggered.
---

# bilibili-hub

> **Modified from**: [jackwener/bilibili-cli](https://github.com/jackwener/bilibili-cli) (Apache-2.0)
>
> This skill simplifies and modifies the original repository as follows:
> - Removed the `browser-cookie3` / `click` / `rich` / `PyYAML` / `qrcode` dependencies
> - Changed cookie authentication to accept a `dict` directly or read from environment variables, without automatic browser extraction
> - Removed the CLI layer (`commands/`), QR login, formatters, and related components
> - Kept all API methods and wrapped them uniformly as synchronous interfaces (`asyncio.run`)
> - Core dependency: `bilibili-api-python`, a third-party SDK that reverse engineers the Bilibili API
> - In the Minis environment, cookies are obtained automatically through `browser_use get_cookies`

---

## File Structure

```
/var/minis/skills/bilibili-hub/
├── SKILL.md
├── pyproject.toml          # bilibili-api-python + aiohttp
└── scripts/
    ├── __init__.py
    ├── exceptions.py       # 6 structured exception types
    ├── payloads.py         # Data structure normalization (normalize_* functions)
    └── client.py           # BiliClient core class (all API methods)
```

---

## Authentication Methods

The Bilibili Web API uses three key cookies:

| Cookie | Description |
|--------|------|
| `SESSDATA` | User session (required for read operations) |
| `bili_jct` | CSRF token (required for write operations: likes, coins, posting dynamics, etc.) |
| `DedeUserID` | User ID (recommended) |
| `buvid3` | Device ID (recommended; reduces the likelihood of triggering risk control) |

### Method 1: Automatically retrieve via `browser_use` (preferred in Minis environments)

1. Use `browser_use navigate` to open `https://www.bilibili.com` and confirm that you are logged in.
2. Use `browser_use get_cookies` to retrieve cookies (raw values do not appear in the conversation).
3. Load the offload env file:

```bash
. /var/minis/offloads/env_cookies_www_bilibili_com_xxx.sh
export BILI_SESSDATA="$COOKIE_SESSDATA"
export BILI_JCT="$COOKIE_BILI_JCT"
export BILI_USERID="$COOKIE_DEDEUSERID"
export BILI_BUVID3="$COOKIE_BUVID3"
```

> **Note**: `get_cookies` only applies to the current page's domain. Navigate to `https://www.bilibili.com` before calling it.

### Method 2: Manually retrieve from browser DevTools

1. Log in to Bilibili, then open DevTools -> Application -> Cookies -> `https://www.bilibili.com`.
2. Find the values of `SESSDATA`, `bili_jct`, and `DedeUserID`.
3. Store them in the Minis environment variables: `BILI_SESSDATA` / `BILI_JCT` / `BILI_USERID`.

### Ways to pass cookies (three methods)

```python
# Method 1: From environment variables (recommended)
client = BiliClient.from_env()

# Method 2: Pass a dict directly
client = BiliClient({
    "SESSDATA":   os.environ["BILI_SESSDATA"],
    "bili_jct":   os.environ["BILI_JCT"],
    "DedeUserID": os.environ["BILI_USERID"],
})

# Method 3: Read-only operations (no write permissions required)
client = BiliClient({"SESSDATA": os.environ["BILI_SESSDATA"]})
```

---

## Quick Start

### Environment setup

```bash
cd /var/minis/skills/bilibili-hub
uv sync
```

### Calling as a Python library

```python
import os, json, sys
sys.path.insert(0, "/var/minis/skills/bilibili-hub")
from scripts.client import BiliClient

client = BiliClient.from_env()

# Current user information
me = client.whoami()
print("User:", me.get("name"), "UID:", me.get("mid"))

# Search videos
videos = client.search_videos("Python Tutorial", count=5)
for v in videos:
    print(f"  {v['bvid']} {v['title']} ({v['duration']})")

# Get video details (including subtitles)
detail = client.get_video("BV1xx411c7mD", subtitle=True)
print(detail["video"]["title"])
print(detail["subtitle"]["text"][:200])

# Popular videos
hot = client.get_hot(count=10)
for v in hot:
    print(f"  {v['bvid']} {v['title']} 👁{v['stats']['view']}")
```

---

## API Method Quick Reference

### Account

| Method | Description |
|------|------|
| `whoami()` | Get information about the currently logged-in user |

### Video

| Method | Description |
|------|------|
| `get_video(bvid, *, subtitle, subtitle_timeline, ai_summary, comments, related)` | Get video details (optional subtitles/AI summary/comments/related videos) |

`bvid` supports a BV number or full URL and is extracted automatically.

### Users

| Method | Description |
|------|------|
| `get_user(uid)` | Get user profile information + following/follower counts |
| `get_user_videos(uid, count=20)` | Get videos posted by a user |

### Search

| Method | Description |
|------|------|
| `search_videos(keyword, page=1, count=20)` | Search videos |
| `search_users(keyword, page=1)` | Search users |

### Discover

| Method | Description |
|------|------|
| `get_hot(page=1, count=20)` | Site-wide popular videos |
| `get_rank(day=3, count=50)` | Site-wide rankings (`day`: 1/3/7) |
| `get_feed(offset=0)` | Following dynamics feed (login required) |
| `get_my_dynamics(offset=0)` | Dynamics I published (login required) |
| `post_dynamic(text)` | Post a text dynamic (login + `bili_jct` required) |
| `delete_dynamic(dynamic_id)` | Delete a dynamic (login + `bili_jct` required) |

### Favorites / History

| Method | Description |
|------|------|
| `get_favorites()` | Get the favorites folder list (login required) |
| `get_favorites(folder_id)` | Get videos in a favorites folder |
| `get_following(page=1)` | Get the following list (login required) |
| `get_watch_later()` | Get the Watch Later list (login required) |
| `get_history()` | Get viewing history (login required) |

### Download

| Method | Description |
|------|------|
| `download_video(bvid, output_dir, filename=None)` | Download the full video (`mp4`), automatically handling DASH merging |
| `download_audio(bvid, output_dir, filename=None)` | Download only the audio stream (`m4a`), suitable for ASR transcription |

**Download process**:
- DASH streams (common): download the video stream and audio stream separately -> merge with `ffmpeg copy` -> if merging fails, keep the silent video
- FLV/MP4 streams (rare): download directly, no merging required
- Without logging in, downloads are limited to 480P; after logging in, 1080P is available (premium members can download higher quality)

| Method | Description |
|------|------|
| `like(bvid)` / `like(bvid, undo=True)` | Like / unlike (requires `bili_jct`) |
| `coin(bvid, num=1)` | Give 1 or 2 coins (requires `bili_jct`) |
| `triple(bvid)` | Perform the one-click triple action (requires `bili_jct`) |
| `unfollow(uid)` | Unfollow a user (requires `bili_jct`) |

---

## Error Handling

```python
from scripts.exceptions import (
    AuthenticationError,  # Cookie missing or expired
    RateLimitError,       # Triggered risk control (412)
    NotFoundError,        # Video/user does not exist
    NetworkError,         # Network/timeout error
    InvalidBvidError,     # Invalid BV number format
    BiliError,            # Other API error (base class)
)

try:
    detail = client.get_video("BV1xx411c7mD")
except AuthenticationError:
    print("Cookie has expired. Please retrieve it again.")
except RateLimitError:
    print("Risk control triggered. Try again later.")
except NotFoundError:
    print("Video does not exist.")
except BiliError as e:
    print(f"API error: {e}")
```

---

## Important Notes

- `SESSDATA` is the minimum requirement for read operations. Write operations (likes, coins, posting dynamics) also require `bili_jct`.
- Cookies are usually valid for several days to several weeks. After they expire, retrieve them again through `browser_use get_cookies`.
- Bilibili applies risk control to high-frequency requests (HTTP 412). An operation interval of at least 1 second is recommended.
- `bilibili-api-python` is a community-maintained reverse-engineering project, and its interfaces may break when Bilibili updates.
- Write operations (coins, triple action, etc.) cannot be reversed. Use them with caution.