1
- import json
2
1
import os
3
2
from datetime import datetime
4
3
from typing import List , Tuple
13
12
14
13
class Bilibili :
15
14
@network_retry
16
- async def get_dynamic (self , dynamic_id : int ):
15
+ async def get_dynamic (self , dynamic_id : str ):
17
16
"""Get dynamic data from API."""
18
17
api = (
19
- "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr"
20
- "/get_dynamic_detail?dynamic_id=" + str (dynamic_id )
18
+ f"https://api.bilibili.com/x/polymer/web-dynamic/v1/detail?id={ dynamic_id } "
21
19
)
22
20
async with Request () as request :
23
21
async with request .get (api ) as response :
24
22
response .raise_for_status ()
25
23
data = await response .json ()
26
24
# For some IDs, the API returns code 0 but empty content
27
- if data ["code" ] == 500207 or (
28
- data ["code" ] == 0 and "card" not in data ["data" ]
29
- ):
25
+ code = data .get ("code" )
26
+ if code == 4101147 or "data" not in data :
30
27
raise NazurinError ("Dynamic not found" )
31
- if data ["code" ] != 0 :
32
- raise NazurinError ("Failed to get dynamic: " + data ["message" ])
33
- card = data ["data" ]["card" ]
34
- desc = card ["desc" ]
35
- card = json .loads (card ["card" ])
36
- card .update (
37
- {
38
- "type" : desc ["type" ],
39
- "dynamic_id_str" : desc ["dynamic_id_str" ],
40
- "view" : desc ["view" ],
41
- "repost" : desc ["repost" ],
42
- "comment" : desc ["comment" ],
43
- "like" : desc ["like" ],
44
- "timestamp" : desc ["timestamp" ],
45
- }
46
- )
47
- if "vip" in card ["user" ]:
48
- del card ["user" ]["vip" ]
49
- return card
28
+ if code != 0 :
29
+ raise NazurinError (
30
+ f"Failed to get dynamic: code = { code } , message = { data ['message' ]} "
31
+ )
32
+ item = data ["data" ]["item" ]
33
+ return self .cleanup_item (item )
50
34
51
- async def fetch (self , dynamic_id : int ) -> Illust :
35
+ async def fetch (self , dynamic_id : str ) -> Illust :
52
36
"""Fetch images and detail."""
53
- card = await self .get_dynamic (dynamic_id )
54
- imgs = self .get_images (card )
55
- caption = self .build_caption (card )
56
- caption ["url" ] = f"https://t .bilibili.com/{ dynamic_id } "
57
- return Illust (imgs , caption , card )
37
+ item = await self .get_dynamic (dynamic_id )
38
+ imgs = self .get_images (item )
39
+ caption = self .build_caption (item )
40
+ caption ["url" ] = f"https://www .bilibili.com/opus /{ dynamic_id } "
41
+ return Illust (imgs , caption , item )
58
42
59
43
@staticmethod
60
- def get_images (card ) -> List [Image ]:
44
+ def get_images (item : dict ) -> List [Image ]:
61
45
"""Get all images in a dynamic card."""
62
- if "item" not in card or "pictures" not in card ["item" ]:
46
+ major_items = item ["modules" ]["module_dynamic" ]["major" ]
47
+ if not major_items :
48
+ raise NazurinError ("No image found" )
49
+ draw_items = major_items ["draw" ]["items" ]
50
+ if not len (draw_items ):
63
51
raise NazurinError ("No image found" )
64
- pics = card ["item" ]["pictures" ]
65
52
imgs = []
66
- for index , pic in enumerate (pics ):
67
- url = pic ["img_src " ]
68
- destination , filename = Bilibili .get_storage_dest (card , pic , index )
69
- size = pic ["img_size " ] * 1024 # size returned by API is in KB
53
+ for index , pic in enumerate (draw_items ):
54
+ url = pic ["src " ]
55
+ destination , filename = Bilibili .get_storage_dest (item , pic , index )
56
+ size = pic ["size " ] * 1024 # size returned by API is in KB
70
57
# Sometimes it returns a wrong size that is not in whole bytes,
71
58
# in this case we just ignore it.
72
59
if size % 1 != 0 :
@@ -78,30 +65,32 @@ def get_images(card) -> List[Image]:
78
65
destination ,
79
66
url + "@518w.jpg" ,
80
67
size ,
81
- pic ["img_width " ],
82
- pic ["img_height " ],
68
+ pic ["width " ],
69
+ pic ["height " ],
83
70
)
84
71
)
85
72
return imgs
86
73
87
74
@staticmethod
88
- def get_storage_dest (card : dict , pic : dict , index : int = 0 ) -> Tuple [str , str ]:
75
+ def get_storage_dest (item : dict , pic : dict , index : int = 0 ) -> Tuple [str , str ]:
89
76
"""
90
77
Format destination and filename.
91
78
"""
92
79
93
- url = pic ["img_src " ]
94
- timestamp = datetime .fromtimestamp (card [ "timestamp " ])
80
+ url = pic ["src " ]
81
+ timestamp = datetime .fromtimestamp (item [ "modules" ][ "module_author" ][ "pub_ts " ])
95
82
basename = os .path .basename (url )
96
83
filename , extension = os .path .splitext (basename )
84
+ user = item ["modules" ]["module_author" ]
97
85
context = {
98
- ** card ,
86
+ "user" : user ,
99
87
# Original filename, without extension
100
88
"filename" : filename ,
101
89
# Image index
102
90
"index" : index ,
103
91
"timestamp" : timestamp ,
104
92
"extension" : extension ,
93
+ "id_str" : item ["id_str" ],
105
94
"pic" : pic ,
106
95
}
107
96
return (
@@ -110,7 +99,21 @@ def get_storage_dest(card: dict, pic: dict, index: int = 0) -> Tuple[str, str]:
110
99
)
111
100
112
101
@staticmethod
113
- def build_caption (card ) -> Caption :
102
+ def build_caption (item : dict ) -> Caption :
103
+ modules = item ["modules" ]
114
104
return Caption (
115
- {"author" : card ["user" ]["name" ], "content" : card ["item" ]["description" ]}
105
+ {
106
+ "author" : "#" + modules ["module_author" ]["name" ],
107
+ "content" : modules ["module_dynamic" ]["desc" ]["text" ],
108
+ }
116
109
)
110
+
111
+ @staticmethod
112
+ def cleanup_item (item : dict ) -> dict :
113
+ try :
114
+ del item ["basic" ]
115
+ del item ["modules" ]["module_author" ]["avatar" ]
116
+ del item ["modules" ]["module_more" ]
117
+ except KeyError :
118
+ pass
119
+ return item
0 commit comments