From 945bf8e1c0f75895bb5ba875a8a045ffd7ccbabd Mon Sep 17 00:00:00 2001 From: sunshine6666666666 Date: Sat, 11 Jul 2026 10:47:39 +0800 Subject: [PATCH] feat(bilibili): support custom video covers --- docs/CLI.md | 2 +- sau_cli.py | 5 +++++ tests/test_sau_bilibili_cli.py | 13 +++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/CLI.md b/docs/CLI.md index 9e65c89..d36ede6 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -81,7 +81,7 @@ SAU_XHS_CREATOR_BASE_URL=https://creator.rednote.com sau xiaohongshu login --acc ```bash sau bilibili login --account sau bilibili check --account -sau bilibili upload-video --account --file videos/demo.mp4 --title "示例标题" --desc "示例简介" --tid 249 --tags 足球,测试 +sau bilibili upload-video --account --file videos/demo.mp4 --title "示例标题" --desc "示例简介" --tid 249 --tags 足球,测试 --thumbnail covers/demo.png ``` 补充说明: diff --git a/sau_cli.py b/sau_cli.py index e5c8727..4cbe854 100644 --- a/sau_cli.py +++ b/sau_cli.py @@ -145,6 +145,7 @@ class BilibiliVideoUploadRequest: tid: int tags: list[str] publish_date: datetime | int + thumbnail_file: Path | None = None @dataclass(slots=True) @@ -501,6 +502,8 @@ async def upload_bilibili_video(request: BilibiliVideoUploadRequest) -> Path: ] if request.tags: arguments.extend(["--tag", ",".join(request.tags)]) + if request.thumbnail_file: + arguments.extend(["--cover", str(request.thumbnail_file)]) if isinstance(request.publish_date, datetime): arguments.extend(["--dtime", str(int(request.publish_date.timestamp()))]) @@ -680,6 +683,7 @@ def build_parser() -> argparse.ArgumentParser: bilibili_upload_video_parser.add_argument("--desc", required=True, help="Video description") bilibili_upload_video_parser.add_argument("--tid", required=True, type=int, help="Bilibili category id") bilibili_upload_video_parser.add_argument("--tags", default="", help="Comma-separated tags, such as tag1,tag2") + bilibili_upload_video_parser.add_argument("--thumbnail", type=existing_file_path, help="Optional Bilibili cover image path") bilibili_upload_video_parser.add_argument("--schedule", type=schedule_value, help=f"Schedule time in {schedule_help}") tencent_parser = platform_parsers.add_parser("tencent", help="Tencent/WeChat Channels operations") @@ -926,6 +930,7 @@ async def dispatch(args: argparse.Namespace) -> int: tid=args.tid, tags=parse_tags(args.tags), publish_date=args.schedule or 0, + thumbnail_file=args.thumbnail, ) await upload_bilibili_video(request) print(f"Bilibili video upload submitted: {request.video_file}") diff --git a/tests/test_sau_bilibili_cli.py b/tests/test_sau_bilibili_cli.py index 723ba30..96cf819 100644 --- a/tests/test_sau_bilibili_cli.py +++ b/tests/test_sau_bilibili_cli.py @@ -1,6 +1,9 @@ import asyncio +import tempfile import unittest from argparse import Namespace +from pathlib import Path +from types import SimpleNamespace from unittest.mock import AsyncMock, patch import sau_cli @@ -43,3 +46,13 @@ class BilibiliCliTests(unittest.TestCase): self.assertFalse(result["success"]) self.assertIn("local interactive terminal", result["message"].lower()) self.assertIn("qrcode.png", result["message"].lower()) + + def test_upload_bilibili_video_passes_thumbnail_to_biliup(self): + with tempfile.TemporaryDirectory() as temp_dir: + account_file = Path(temp_dir) / "account.json" + account_file.write_text("{}", encoding="utf-8") + request = sau_cli.BilibiliVideoUploadRequest("creator", Path("demo.mp4"), "hello", "hello", 249, ["test"], 0, Path("cover.png")) + with patch("sau_cli.resolve_account_file", return_value=account_file), patch("sau_cli.run_biliup_command", return_value=SimpleNamespace(returncode=0, stdout="", stderr="")) as run_biliup: + asyncio.run(sau_cli.upload_bilibili_video(request)) + self.assertIn("--cover", run_biliup.call_args.args[0]) + self.assertIn("cover.png", run_biliup.call_args.args[0])