From c6060f62c07d1bfbf43691be5f715973c782d1f9 Mon Sep 17 00:00:00 2001 From: John Howe <89397553+timerring@users.noreply.github.com> Date: Tue, 14 Jan 2025 18:47:48 +0800 Subject: [PATCH] feat: add vid convert module --- bilitool/cli.py | 9 ++++++ bilitool/utils/check_format.py | 56 ++++++++++++++++++++++++++++++++++ tests/test_utils.py | 10 ++++++ 3 files changed, 75 insertions(+) create mode 100644 bilitool/utils/check_format.py diff --git a/bilitool/cli.py b/bilitool/cli.py index 7383213..e386422 100644 --- a/bilitool/cli.py +++ b/bilitool/cli.py @@ -15,6 +15,7 @@ from bilitool.controller.download_controller import DownloadController from bilitool.utils.get_ip_info import IPInfo from bilitool.feed.bili_video_list import BiliVideoList +from bilitool.utils.check_format import CheckFormat def cli(): logging.basicConfig( @@ -64,6 +65,10 @@ def cli(): list_parser.add_argument('--size', type=int, default=20, help='(default is 20) the size of video list') list_parser.add_argument('--status', default='pubed,not_pubed,is_pubing', help='(default is all) the status of video list: pubed, not_pubed, is_pubing') + # Convert subcommand + convert_parser = subparsers.add_parser('convert', help='Convert between avid and bvid') + convert_parser.add_argument('vid', help='The avid or bvid of the video') + # IP subcommand ip_parser = subparsers.add_parser('ip', help='Get the ip info') ip_parser.add_argument('--ip', default='', help='(default is your request ip) The ip address') @@ -112,6 +117,10 @@ def cli(): if args.subcommand == 'list': bili = BiliVideoList() bili.print_video_list_info(bili.get_member_video_list(args.size, args.status)) + + if args.subcommand == 'convert': + check_format = CheckFormat() + check_format.convert_bv_and_av(args.vid) if __name__ == '__main__': cli() \ No newline at end of file diff --git a/bilitool/utils/check_format.py b/bilitool/utils/check_format.py new file mode 100644 index 0000000..2a7d934 --- /dev/null +++ b/bilitool/utils/check_format.py @@ -0,0 +1,56 @@ +# Copyright (c) 2025 bilitool + +class CheckFormat(object): + def __init__(self): + self.XOR_CODE = 23442827791579 + self.MASK_CODE = 2251799813685247 + self.MAX_AID = 1 << 51 + self.ALPHABET = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf" + self.ENCODE_MAP = 8, 7, 0, 5, 1, 3, 2, 4, 6 + self.DECODE_MAP = tuple(reversed(self.ENCODE_MAP)) + + self.BASE = len(self.ALPHABET) + self.PREFIX = "BV1" + self.PREFIX_LEN = len(self.PREFIX) + self.CODE_LEN = len(self.ENCODE_MAP) + + @staticmethod + def is_bvid(bvid: str) -> bool: + if len(bvid) != 12: + return False + if bvid[0:2] != 'BV': + return False + return True + + @staticmethod + def is_chinese(word: str) -> bool: + for ch in word: + if '\u4e00' <= ch <= '\u9fff': + return True + return False + + # https://github.com/SocialSisterYi/bilibili-API-collect/blob/e5fbfed42807605115c6a9b96447f6328ca263c5/docs/misc/bvid_desc.md + + def av2bv(self, aid: int) -> str: + bvid = [""] * 9 + tmp = (self.MAX_AID | aid) ^ self.XOR_CODE + for i in range(self.CODE_LEN): + bvid[self.ENCODE_MAP[i]] = self.ALPHABET[tmp % self.BASE] + tmp //= self.BASE + return self.PREFIX + "".join(bvid) + + def bv2av(self, bvid: str) -> int: + assert bvid[:3] == self.PREFIX + + bvid = bvid[3:] + tmp = 0 + for i in range(self.CODE_LEN): + idx = self.ALPHABET.index(bvid[self.DECODE_MAP[i]]) + tmp = tmp * self.BASE + idx + return (tmp & self.MASK_CODE) ^ self.XOR_CODE + + def convert_bv_and_av(self, vid: str): + if self.is_bvid(str(vid)): + print("The avid of the video is: ", self.bv2av(str(vid))) + else: + print("The bvid of the video is: ", self.av2bv(int(vid))) diff --git a/tests/test_utils.py b/tests/test_utils.py index 8dab479..6f99571 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -9,3 +9,13 @@ class TestIPInfo(unittest.TestCase): def test_get_ip_address(self): self.assertEqual(IPInfo.get_ip_address('12.12.12.12'), ('12.12.12.12', 'att.com', '美国阿拉斯加州安克雷奇', '61.108841,-149.373145')) + + +class TestCheckFormat(unittest.TestCase): + def test_av2bv(self): + check_format = CheckFormat() + self.assertEqual(check_format.av2bv(2), 'BV1xx411c7mD') + + def test_bv2av(self): + check_format = CheckFormat() + self.assertEqual(check_format.bv2av('BV1y7411Q7Eq'), 99999999)