🐛 fix AssetsService

🐛 修复 `AssetsService`
This commit is contained in:
Karako 2022-12-07 16:40:30 +08:00 committed by GitHub
parent 7d1c6049fe
commit 330a7b22e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 75 deletions

View File

@ -14,7 +14,7 @@ from aiofiles import open as async_open
from aiofiles.os import remove as async_remove from aiofiles.os import remove as async_remove
from enkanetwork import Assets as EnkaAssets from enkanetwork import Assets as EnkaAssets
from enkanetwork.model.assets import CharacterAsset as EnkaCharacterAsset from enkanetwork.model.assets import CharacterAsset as EnkaCharacterAsset
from httpx import AsyncClient, HTTPError, HTTPStatusError, URL from httpx import AsyncClient, HTTPError, HTTPStatusError, TransportError, URL
from typing_extensions import Self from typing_extensions import Self
from core.service import Service from core.service import Service
@ -28,7 +28,9 @@ from utils.log import logger
from utils.typedefs import StrOrInt, StrOrURL from utils.typedefs import StrOrInt, StrOrURL
if TYPE_CHECKING: if TYPE_CHECKING:
from httpx import Response
from multiprocessing.synchronize import RLock from multiprocessing.synchronize import RLock
ICON_TYPE = Union[Callable[[bool], Awaitable[Optional[Path]]], Callable[..., Awaitable[Optional[Path]]]] ICON_TYPE = Union[Callable[[bool], Awaitable[Optional[Path]]], Callable[..., Awaitable[Optional[Path]]]]
NAME_MAP_TYPE = Dict[str, StrOrURL] NAME_MAP_TYPE = Dict[str, StrOrURL]
@ -111,6 +113,18 @@ class _AssetsService(ABC):
cls._dir = ASSETS_PATH.joinpath(cls.type) # 图标保存的文件夹 cls._dir = ASSETS_PATH.joinpath(cls.type) # 图标保存的文件夹
cls._dir.mkdir(exist_ok=True, parents=True) cls._dir.mkdir(exist_ok=True, parents=True)
async def _request(self, url: str, interval: float = 0.2) -> "Response":
error = None
for _ in range(5):
try:
return await self.client.get(url, follow_redirects=False)
except (TransportError, SSLZeroReturnError) as e:
error = e
await asyncio.sleep(interval)
continue
if error is not None:
raise error
async def _download(self, url: StrOrURL, path: Path, retry: int = 5) -> Path | None: async def _download(self, url: StrOrURL, path: Path, retry: int = 5) -> Path | None:
"""从 url 下载图标至 path""" """从 url 下载图标至 path"""
logger.debug(f"正在从 {url} 下载图标至 {path}") logger.debug(f"正在从 {url} 下载图标至 {path}")
@ -132,42 +146,31 @@ class _AssetsService(ABC):
await file.write(response.content) # 保存图标 await file.write(response.content) # 保存图标
return path.resolve() return path.resolve()
async def _get_from_ambr(self, item: str) -> str | None: # pylint: disable=W0613,R0201 async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]: # pylint: disable=W0613,R0201
"""从 ambr.top 上爬取""" """从 ambr.top 上获取目标链接"""
return None yield None
async def _get_from_enka(self, item: str) -> str | None: # pylint: disable=W0613,R0201 async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]: # pylint: disable=W0613,R0201
"""从 enke.network 上爬取""" """从 enke.network 上获取目标链接"""
return None yield None
async def _get_from_honey(self, item: str) -> str | None: async def _get_from_honey(self, item: str) -> AsyncIterator[str | None]:
"""从 honey 上爬取""" """从 honey 上获取目标链接"""
try: if (honey_name := self.honey_name_map.get(item, None)) is not None:
honey_name = self.honey_name_map.get(item, None) yield HONEY_HOST.join(f"img/{honey_name}.png")
except IndexError: yield HONEY_HOST.join(f"img/{honey_name}.webp")
return None
if honey_name is not None:
try:
result = HONEY_HOST.join(f"img/{honey_name}.png")
response = await self.client.get(result, follow_redirects=False)
response.raise_for_status()
except HTTPStatusError:
return None
if response.status_code == 200:
return result
return HONEY_HOST.join(f"img/{honey_name}.webp")
async def _download_url_generator(self, item: str) -> AsyncIterator[str]: async def _download_url_generator(self, item: str) -> AsyncIterator[str]:
# 获取当前 `AssetsService` 的所有爬虫
for func in map(lambda x: getattr(self, x), sorted(filter(lambda x: x.startswith("_get_from_"), dir(self)))): for func in map(lambda x: getattr(self, x), sorted(filter(lambda x: x.startswith("_get_from_"), dir(self)))):
if (url := await func(item)) is not None: async for url in func(item):
try: if url is not None:
response = await self.client.get(url := str(url)) try:
response.raise_for_status() response = await self._request(url := str(url))
if response.status_code == 200: response.raise_for_status()
yield url yield url
except HTTPStatusError: except HTTPStatusError:
continue continue
async def _get_download_url(self, item: str) -> str | None: async def _get_download_url(self, item: str) -> str | None:
"""获取图标的下载链接""" """获取图标的下载链接"""
@ -269,15 +272,13 @@ class _AvatarAssets(_AssetsService):
result._enka_api = self._enka_api result._enka_api = self._enka_api
return result return result
async def _get_from_ambr(self, item: str) -> str | None: async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
if item in {"icon", "side", "gacha"}: if item in {"icon", "side", "gacha"}:
return str(AMBR_HOST.join(f"assets/UI/{self.game_name_map[item]}.png")) yield str(AMBR_HOST.join(f"assets/UI/{self.game_name_map[item]}.png"))
async def _get_from_enka(self, item: str) -> str | None: async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]:
item = "banner" if item == "gacha" else item if (item_id := self.game_name_map.get(item, None)) is not None:
# noinspection PyUnboundLocalVariable yield str(ENKA_HOST.join(f"ui/{item_id}.png"))
if self.enka is not None and item in (data := self.enka.images.dict()).keys() and (url := data[item]["url"]):
return str(url)
@cached_property @cached_property
def honey_name_map(self) -> dict[str, str]: def honey_name_map(self) -> dict[str, str]:
@ -331,13 +332,13 @@ class _WeaponAssets(_AssetsService):
result.id = target result.id = target
return result return result
async def _get_from_ambr(self, item: str) -> str | None: async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
if item == "icon": if item == "icon":
return str(AMBR_HOST.join(f"assets/UI/{self.game_name_map.get(item)}.png")) yield str(AMBR_HOST.join(f"assets/UI/{self.game_name_map.get(item)}.png"))
async def _get_from_enka(self, item: str) -> str | None: async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]:
if item in self.game_name_map: if item in self.game_name_map:
return str(ENKA_HOST.join(f"ui/{self.game_name_map.get(item)}.png")) yield str(ENKA_HOST.join(f"ui/{self.game_name_map.get(item)}.png"))
@cached_property @cached_property
def honey_name_map(self) -> dict[str, str]: def honey_name_map(self) -> dict[str, str]:
@ -374,21 +375,13 @@ class _MaterialAssets(_AssetsService):
result.id = target result.id = target
return result return result
async def _get_from_ambr(self, item: str) -> str | None: async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
if item == "icon": if item == "icon":
return str(AMBR_HOST.join(f"assets/UI/{self.game_name_map.get(item)}.png")) yield str(AMBR_HOST.join(f"assets/UI/{self.game_name_map.get(item)}.png"))
async def _get_from_honey(self, item: str) -> str | None: async def _get_from_honey(self, item: str) -> AsyncIterator[str | None]:
try: yield HONEY_HOST.join(f"/img/{self.honey_name_map.get(item)}.png")
result = HONEY_HOST.join(f"/img/{self.honey_name_map.get(item)}.png") yield HONEY_HOST.join(f"/img/{self.honey_name_map.get(item)}.webp")
response = await self.client.get(result, follow_redirects=False)
response.raise_for_status()
except HTTPStatusError:
return None
if response.status_code == 200:
return result
return HONEY_HOST.join(f"/img/{self.honey_name_map.get(item)}.webp")
class _ArtifactAssets(_AssetsService): class _ArtifactAssets(_AssetsService):
@ -415,13 +408,13 @@ class _ArtifactAssets(_AssetsService):
def game_name(self) -> str: def game_name(self) -> str:
return f"UI_RelicIcon_{self.id}" return f"UI_RelicIcon_{self.id}"
async def _get_from_enka(self, item: str) -> str | None: async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]:
if item in self.game_name_map: if item in self.game_name_map:
return str(ENKA_HOST.join(f"ui/{self.game_name_map.get(item)}.png")) yield str(ENKA_HOST.join(f"ui/{self.game_name_map.get(item)}.png"))
async def _get_from_ambr(self, item: str) -> str | None: async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
if item in self.game_name_map: if item in self.game_name_map:
return str(AMBR_HOST.join(f"assets/UI/reliquary/{self.game_name_map[item]}.png")) yield str(AMBR_HOST.join(f"assets/UI/reliquary/{self.game_name_map[item]}.png"))
@cached_property @cached_property
def game_name_map(self) -> dict[str, str]: def game_name_map(self) -> dict[str, str]:
@ -470,14 +463,13 @@ class _NamecardAssets(_AssetsService):
result.enka = DEFAULT_EnkaAssets.namecards(target) result.enka = DEFAULT_EnkaAssets.namecards(target)
return result return result
async def _get_from_ambr(self, item: str) -> str | None: async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
if item == "profile": if item == "profile":
return AMBR_HOST.join(f"assets/UI/namecard/{self.game_name_map[item]}.png.png") yield AMBR_HOST.join(f"assets/UI/namecard/{self.game_name_map[item]}.png.png")
async def _get_from_enka(self, item: str) -> str | None: async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]:
url = getattr(self.enka, {"profile": "banner"}.get(item, item), None) if (url := getattr(self.enka, {"profile": "banner"}.get(item, item), None)) is not None:
if url is not None: yield str(url)
return str(url)
@cached_property @cached_property
def game_name_map(self) -> dict[str, str]: def game_name_map(self) -> dict[str, str]:

View File

@ -3,7 +3,7 @@ import datetime
import json import json
from os import PathLike from os import PathLike
from pathlib import Path from pathlib import Path
from typing import List, Tuple, Optional, IO, Union from typing import Dict, IO, List, Optional, Tuple, Union
import aiofiles import aiofiles
from genshin import Client, InvalidAuthkey from genshin import Client, InvalidAuthkey
@ -16,25 +16,25 @@ from metadata.shortname import roleToId, weaponToId
from modules.gacha_log.const import GACHA_TYPE_LIST, PAIMONMOE_VERSION from modules.gacha_log.const import GACHA_TYPE_LIST, PAIMONMOE_VERSION
from modules.gacha_log.error import ( from modules.gacha_log.error import (
GachaLogAccountNotFound, GachaLogAccountNotFound,
GachaLogInvalidAuthkey,
GachaLogException, GachaLogException,
GachaLogFileError, GachaLogFileError,
GachaLogInvalidAuthkey,
GachaLogMixedProvider,
GachaLogNotFound, GachaLogNotFound,
PaimonMoeGachaLogFileError, PaimonMoeGachaLogFileError,
GachaLogMixedProvider,
) )
from modules.gacha_log.models import ( from modules.gacha_log.models import (
GachaItem,
FiveStarItem, FiveStarItem,
FourStarItem, FourStarItem,
Pool, GachaItem,
GachaLogInfo, GachaLogInfo,
UIGFGachaType,
ItemType,
ImportType, ImportType,
UIGFModel, ItemType,
Pool,
UIGFGachaType,
UIGFInfo, UIGFInfo,
UIGFItem, UIGFItem,
UIGFModel,
) )
from utils.const import PROJECT_ROOT from utils.const import PROJECT_ROOT
@ -600,7 +600,7 @@ class GachaLog:
} }
@staticmethod @staticmethod
def convert_xlsx_to_uigf(file: Union[str, PathLike[str], IO[bytes]], zh_dict: dict) -> dict: def convert_xlsx_to_uigf(file: Union[str, PathLike, IO[bytes]], zh_dict: Dict) -> Dict:
"""转换 paimone.moe 或 非小酋 导出 xlsx 数据为 UIGF 格式 """转换 paimone.moe 或 非小酋 导出 xlsx 数据为 UIGF 格式
:param file: 导出的 xlsx 文件 :param file: 导出的 xlsx 文件
:param zh_dict: :param zh_dict:

View File

@ -15,7 +15,7 @@ import ujson as json
from aiofiles import open as async_open from aiofiles import open as async_open
from arkowrapper import ArkoWrapper from arkowrapper import ArkoWrapper
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from genshin import Client, InvalidCookies, GenshinException from genshin import Client, GenshinException, InvalidCookies
from genshin.models import Character from genshin.models import Character
from httpx import AsyncClient, HTTPError from httpx import AsyncClient, HTTPError
from pydantic import BaseModel from pydantic import BaseModel