support: some icons

This commit is contained in:
xtaodada 2023-04-27 16:44:27 +08:00
parent 0c9f1f8b86
commit faf212844b
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
15 changed files with 233 additions and 5 deletions

View File

@ -25,6 +25,7 @@ jobs:
- name: Fetch Remote Files
run: |
python main.py
python fix_data.py
- name: Commit changes
uses: EndBug/add-and-commit@v9

13
fix_data.py Normal file
View File

@ -0,0 +1,13 @@
import asyncio
from res_func.avatar import fix_avatar_config
from res_func.light_cone import fix_light_cone_config
async def main():
await fix_avatar_config()
await fix_light_cone_config()
if __name__ == '__main__':
asyncio.run(main())

3
func/README.md Normal file
View File

@ -0,0 +1,3 @@
# wiki 解析
通过米游社官方 wiki 解析部分数据

View File

@ -16,4 +16,4 @@ headers = {
'sec-fetch-site': 'same-site',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
}
client = AsyncClient(headers=headers, timeout=30)
client = AsyncClient(headers=headers, timeout=120.0)

View File

@ -27,7 +27,6 @@ async def fetch_avatars(data: Children):
avatar = Avatar(
id=content.content_id,
name=content.title,
icon=content.icon,
quality=m_quality,
element=m_element,
destiny=m_destiny,

View File

@ -1,7 +1,7 @@
import asyncio
import re
from pathlib import Path
from typing import List
from typing import List, Dict
import aiofiles
import ujson
@ -15,6 +15,7 @@ from models.light_cone import LightCone, LightConePromote, LightConeItem
from models.wiki import Children
all_light_cones: List[LightCone] = []
all_light_cones_name: Dict[str, LightCone] = {}
async def fetch_light_cones(data: Children):
@ -32,6 +33,7 @@ async def fetch_light_cones(data: Children):
promote=[],
)
all_light_cones.append(light_cone)
all_light_cones_name[light_cone.name] = light_cone
def parse_promote(light_cone: LightCone, soup: BeautifulSoup) -> None:
@ -109,6 +111,9 @@ async def dump_light_cones(path: Path):
async def read_light_cones(path: Path):
async with aiofiles.open(path, "r", encoding="utf-8") as f:
data = ujson.loads(await f.read())
all_light_cones.clear()
all_light_cones_name.clear()
for light_cone in data:
m = LightCone(**light_cone)
all_light_cones.append(m)
all_light_cones_name[m.name] = m

View File

@ -44,8 +44,6 @@ class Avatar(BaseModel):
"""角色ID"""
name: str
"""名称"""
icon: str
"""图标"""
quality: Quality
"""品质"""
destiny: Destiny

24
models/avatar_config.py Normal file
View File

@ -0,0 +1,24 @@
from typing import List
from pydantic import BaseModel
class AvatarName(BaseModel):
Hash: int
class AvatarConfig(BaseModel):
name: str = ""
AvatarID: int
AvatarName: AvatarName
AvatarVOTag: str
Release: bool
class AvatarIcon(BaseModel):
id: int
"""角色ID"""
name: str
"""名称"""
icon: List[str]
"""图标(从小到大)"""

View File

@ -0,0 +1,12 @@
from typing import List
from pydantic import BaseModel
class LightConeIcon(BaseModel):
id: int
"""光锥ID"""
name: str
"""名称"""
icon: List[str]
"""图标(从小到大)"""

3
res_func/README.md Normal file
View File

@ -0,0 +1,3 @@
# 素材解析
通过 **** 和 https://starrailstation.com 解析图片素材

0
res_func/__init__.py Normal file
View File

96
res_func/avatar.py Normal file
View File

@ -0,0 +1,96 @@
import asyncio
from pathlib import Path
from typing import Dict, List
import aiofiles
import ujson
from bs4 import BeautifulSoup, Tag
from func.fetch_avatars import read_avatars, all_avatars_name, dump_avatars
from .client import client
from .url import avatar_config, text_map, base_station_url, avatar_url
from models.avatar_config import AvatarConfig, AvatarIcon
async def fetch_text_map() -> Dict[str, str]:
res = await client.get(text_map)
return res.json()
async def fetch_config(text_map: Dict[str, str]) -> List[AvatarConfig]:
res = await client.get(avatar_config)
data = res.json()
datas = []
for i in data.values():
a = AvatarConfig(**i)
a.name = text_map[str(a.AvatarName.Hash)]
datas.append(a)
return datas
async def parse_station(datas, tag: Tag, avatar: AvatarConfig):
second_pic = f'{base_station_url}{tag.find("img").get("src")}'
html = await client.get(f'{base_station_url}{tag.get("href")}')
soup = BeautifulSoup(html.text, "lxml")
text = soup.find("div", {"class": "a6678 a4af5"}).get("style")
third_pic = f'{base_station_url}{text[text.find("(") + 2:text.find(")") - 1]}' if text else ""
first_pic = f'{base_station_url}{soup.find("img", {"class": "ac39b a6602"}).get("src")}'
datas.append(
AvatarIcon(
id=avatar.AvatarID,
name=avatar.name,
icon=[first_pic, second_pic, third_pic],
)
)
async def dump_icons(path: Path, datas: List[AvatarIcon]):
data = [icon.dict() for icon in datas]
data.sort(key=lambda x: x["id"])
async with aiofiles.open(path, "w", encoding="utf-8") as f:
await f.write(ujson.dumps(data, indent=4, ensure_ascii=False))
async def fetch_station(configs_map: Dict[str, AvatarConfig]) -> List[AvatarIcon]:
print("开始获取角色素材")
html = await client.get(avatar_url)
soup = BeautifulSoup(html.text, "lxml")
avatars = soup.find_all("a", {"class": "char-entry-select-option"})
tasks = []
datas: List[AvatarIcon] = []
ktz = False
for avatar in avatars:
name = avatar.find("span").get_text().strip()
if name == "开拓者" and ktz:
continue
if avatar_model := configs_map.get(name):
tasks.append(parse_station(datas, avatar, avatar_model))
if name == "开拓者":
ktz = True
else:
print(f"未找到角色 {name} 的数据")
await asyncio.gather(*tasks)
return datas
async def fix_avatar_config():
text_map_data = await fetch_text_map()
configs = await fetch_config(text_map_data)
configs_map: Dict[str, AvatarConfig] = {config.name: config for config in configs}
configs_map["开拓者"] = configs_map["{NICKNAME}"]
print(f"读取到原始数据:{list(configs_map.keys())}")
data_path = Path("data")
await read_avatars(data_path / "avatars.json")
for key, value in all_avatars_name.items():
if key.startswith("开拓者"):
config = configs_map["{NICKNAME}"]
config.name = "开拓者"
else:
config = configs_map.get(key)
if config is None:
print(f"错误:未找到角色 {key} 的配置")
continue
value.id = config.AvatarID
icons = await fetch_station(configs_map)
await dump_icons(data_path / "avatar_icons.json", icons)
await dump_avatars(data_path / "avatars.json")

3
res_func/client.py Normal file
View File

@ -0,0 +1,3 @@
from httpx import AsyncClient
client = AsyncClient(timeout=120.0)

61
res_func/light_cone.py Normal file
View File

@ -0,0 +1,61 @@
import asyncio
from pathlib import Path
from typing import Dict, List
import aiofiles
import ujson
from bs4 import BeautifulSoup, Tag
from func.fetch_light_cones import read_light_cones, all_light_cones_name, dump_light_cones
from .client import client
from .url import base_station_url, light_cone_url
from models.light_cone_config import LightConeIcon
async def parse_station(icon: LightConeIcon, tag: Tag):
html = await client.get(f'{base_station_url}{tag.get("href")}')
soup = BeautifulSoup(html.text, "lxml")
first_pic = f'{base_station_url}{soup.find("img", {"class": "standard-icon a6602"}).get("src")}'
second_pic = f'{base_station_url}{soup.find("img", {"class": "a2b16 mobile-only-elem ab8c3"}).get("src")}'
icon.icon = [first_pic, second_pic]
async def dump_icons(path: Path, datas: List[LightConeIcon]):
data = [icon.dict() for icon in datas]
data.sort(key=lambda x: x["id"])
async with aiofiles.open(path, "w", encoding="utf-8") as f:
await f.write(ujson.dumps(data, indent=4, ensure_ascii=False))
async def fetch_station() -> List[LightConeIcon]:
print("开始获取光锥素材")
html = await client.get(light_cone_url)
soup = BeautifulSoup(html.text, "lxml")
light_cones = soup.find_all("a", {"class": "a4041 ae026 a5abc"})
tasks = []
datas: List[LightConeIcon] = []
for light_cone in light_cones:
name = light_cone.find("span").get_text().strip()
url = light_cone.get("href")
nid = int(url.split("/")[-1])
if light_cone_model := all_light_cones_name.get(name):
light_cone_model.id = nid
else:
print(f"wiki 未找到光锥数据 {name} ,修复 id 失败")
icon = LightConeIcon(
id=nid,
name=name,
icon=[]
)
datas.append(icon)
tasks.append(parse_station(icon, light_cone))
await asyncio.gather(*tasks)
return datas
async def fix_light_cone_config():
data_path = Path("data")
await read_light_cones(data_path / "light_cones.json")
icons = await fetch_station()
await dump_icons(data_path / "light_cone_icons.json", icons)
await dump_light_cones(data_path / "light_cones.json")

10
res_func/url.py Normal file
View File

@ -0,0 +1,10 @@
import base64
base_data_url = base64.b64decode(
"aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0RpbWJyZWF0aC9TdGFyUmFpbERhdGEvbWFzdGVyLw=="
).decode("utf-8")
avatar_config = f"{base_data_url}ExcelOutput/AvatarConfig.json"
text_map = f"{base_data_url}TextMap/TextMapCN.json"
base_station_url = "https://starrailstation.com"
avatar_url = f"{base_station_url}/cn/characters"
light_cone_url = f"{base_station_url}/cn/equipment"