'commit'
This commit is contained in:
Binary file not shown.
@@ -19,7 +19,7 @@ from Apps.XinDianTu.Config.Setting import (
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XinDianTuReadImageKit:
|
||||
class ReadImageKit:
|
||||
_client = OpenAI(
|
||||
api_key=ALY_LLM_API_KEY,
|
||||
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
@@ -101,7 +101,7 @@ class XinDianTuReadImageKit:
|
||||
|
||||
@staticmethod
|
||||
def _add_center(obj, device_info):
|
||||
return XinDianTuReadImageKit._add_click_point(obj, device_info, anchor="center")
|
||||
return ReadImageKit._add_click_point(obj, device_info, anchor="center")
|
||||
|
||||
@staticmethod
|
||||
def _add_click_point(obj, device_info, anchor: str = "center"):
|
||||
@@ -210,7 +210,7 @@ class XinDianTuReadImageKit:
|
||||
async def find_all_time_button_coordinate(image_url: str, device_info: dict = None) -> dict:
|
||||
if device_info is None:
|
||||
logger.warning("未提供动态设备信息,使用通用回退配置。")
|
||||
device_info = XinDianTuReadImageKit._FALLBACK_DEVICE_INFO
|
||||
device_info = ReadImageKit._FALLBACK_DEVICE_INFO
|
||||
|
||||
prompt = (
|
||||
"仅输出JSON对象(不含任何说明文字),请找到图片中带有“全部时段”字样的按钮区域(通常在价格表下方,是一个带有右箭头的文字按钮)。\n"
|
||||
@@ -226,7 +226,7 @@ class XinDianTuReadImageKit:
|
||||
loop = asyncio.get_event_loop()
|
||||
resp = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: XinDianTuReadImageKit._client.chat.completions.create(
|
||||
lambda: ReadImageKit._client.chat.completions.create(
|
||||
model=VL_MODEL_NAME,
|
||||
messages=[
|
||||
{
|
||||
@@ -247,12 +247,12 @@ class XinDianTuReadImageKit:
|
||||
)
|
||||
|
||||
content = resp.choices[0].message.content or ""
|
||||
raw = XinDianTuReadImageKit._extract_json(content)
|
||||
raw = ReadImageKit._extract_json(content)
|
||||
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
if isinstance(data, dict) and (data.get("bounds") or data.get("bounds_norm")):
|
||||
data = XinDianTuReadImageKit._add_center(data, device_info)
|
||||
data = ReadImageKit._add_center(data, device_info)
|
||||
# 只返回中心坐标
|
||||
return {
|
||||
"uia_center_x": data.get("uia_center_x"),
|
||||
@@ -268,7 +268,7 @@ class XinDianTuReadImageKit:
|
||||
async def find_station_coordinate_first_page(image_url: str, station_name: str, device_info: dict = None) -> dict:
|
||||
if device_info is None:
|
||||
logger.warning("未提供动态设备信息,使用通用回退配置。")
|
||||
device_info = XinDianTuReadImageKit._FALLBACK_DEVICE_INFO
|
||||
device_info = ReadImageKit._FALLBACK_DEVICE_INFO
|
||||
|
||||
prompt = (
|
||||
"仅输出JSON对象(不含任何说明文字)。"
|
||||
@@ -283,7 +283,7 @@ class XinDianTuReadImageKit:
|
||||
loop = asyncio.get_event_loop()
|
||||
resp = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: XinDianTuReadImageKit._client.chat.completions.create(
|
||||
lambda: ReadImageKit._client.chat.completions.create(
|
||||
model=VL_MODEL_NAME,
|
||||
messages=[
|
||||
{
|
||||
@@ -299,14 +299,14 @@ class XinDianTuReadImageKit:
|
||||
)
|
||||
|
||||
content = resp.choices[0].message.content or ""
|
||||
raw = XinDianTuReadImageKit._extract_json(content)
|
||||
raw = ReadImageKit._extract_json(content)
|
||||
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
if isinstance(data, dict) and (
|
||||
data.get("station_name_bounds") or data.get("station_name_bounds_norm") or data.get(
|
||||
"bounds") or data.get("bounds_norm")):
|
||||
data = XinDianTuReadImageKit._add_click_point(data, device_info, anchor="station_text")
|
||||
data = ReadImageKit._add_click_point(data, device_info, anchor="station_text")
|
||||
return {
|
||||
"uia_center_x": data.get("uia_center_x"),
|
||||
"uia_center_y": data.get("uia_center_y")
|
||||
@@ -319,12 +319,12 @@ class XinDianTuReadImageKit:
|
||||
async def parse_first_level_image_url(image_url: str, device_info: dict = None) -> list:
|
||||
if device_info is None:
|
||||
logger.warning("未提供动态设备信息,使用通用回退配置。")
|
||||
device_info = XinDianTuReadImageKit._FALLBACK_DEVICE_INFO
|
||||
device_info = ReadImageKit._FALLBACK_DEVICE_INFO
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
resp = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: XinDianTuReadImageKit._client.chat.completions.create(
|
||||
lambda: ReadImageKit._client.chat.completions.create(
|
||||
model=VL_MODEL_NAME,
|
||||
messages=[
|
||||
{
|
||||
@@ -337,7 +337,7 @@ class XinDianTuReadImageKit:
|
||||
},
|
||||
},
|
||||
{"type": "text", "text": json.dumps(device_info, ensure_ascii=False)},
|
||||
{"type": "text", "text": XinDianTuReadImageKit._prompt},
|
||||
{"type": "text", "text": ReadImageKit._prompt},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -345,18 +345,18 @@ class XinDianTuReadImageKit:
|
||||
)
|
||||
|
||||
content = resp.choices[0].message.content or ""
|
||||
raw = XinDianTuReadImageKit._extract_json(content)
|
||||
raw = ReadImageKit._extract_json(content)
|
||||
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
if isinstance(data, list):
|
||||
# 过滤掉 None (即被 _add_click_point 排除掉的顶部项)
|
||||
data = [XinDianTuReadImageKit._add_click_point(x, device_info, anchor="station_text") for x in data if
|
||||
data = [ReadImageKit._add_click_point(x, device_info, anchor="station_text") for x in data if
|
||||
x.get("b_use", 1) == 1]
|
||||
data = [x for x in data if x is not None]
|
||||
elif isinstance(data, dict):
|
||||
if data.get("b_use", 1) == 1:
|
||||
data = XinDianTuReadImageKit._add_click_point(data, device_info, anchor="station_text")
|
||||
data = ReadImageKit._add_click_point(data, device_info, anchor="station_text")
|
||||
data = [data] if data is not None else []
|
||||
else:
|
||||
data = []
|
||||
@@ -397,7 +397,7 @@ class XinDianTuReadImageKit:
|
||||
if name:
|
||||
idxs.append(i)
|
||||
tasks.append(asyncio.create_task(
|
||||
XinDianTuReadImageKit.find_station_coordinate_first_page(image_url, name, device_info)
|
||||
ReadImageKit.find_station_coordinate_first_page(image_url, name, device_info)
|
||||
))
|
||||
if tasks:
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
@@ -418,7 +418,7 @@ class XinDianTuReadImageKit:
|
||||
"""
|
||||
统一入口:混合识别模式
|
||||
"""
|
||||
return await XinDianTuReadImageKit.get_stations_hybrid(image_path, uploader, cdn_domain)
|
||||
return await ReadImageKit.get_stations_hybrid(image_path, uploader, cdn_domain)
|
||||
|
||||
@staticmethod
|
||||
async def parse_vl_image(vl_image_url, json_metadata, device_info=None):
|
||||
@@ -453,7 +453,7 @@ class XinDianTuReadImageKit:
|
||||
loop = asyncio.get_event_loop()
|
||||
resp = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: XinDianTuReadImageKit._client.chat.completions.create(
|
||||
lambda: ReadImageKit._client.chat.completions.create(
|
||||
model=VL_MODEL_NAME,
|
||||
messages=[
|
||||
{
|
||||
@@ -468,7 +468,7 @@ class XinDianTuReadImageKit:
|
||||
)
|
||||
|
||||
content = resp.choices[0].message.content or ""
|
||||
raw = XinDianTuReadImageKit._extract_json(content)
|
||||
raw = ReadImageKit._extract_json(content)
|
||||
|
||||
final_stations = []
|
||||
try:
|
||||
@@ -529,7 +529,7 @@ class XinDianTuReadImageKit:
|
||||
混合识别模式:图形学切片 + 大模型小图 OCR
|
||||
"""
|
||||
# 1. 图形学切片
|
||||
segments = XinDianTuReadImageKit.get_card_segments(image_path)
|
||||
segments = ReadImageKit.get_card_segments(image_path)
|
||||
if not segments:
|
||||
return []
|
||||
|
||||
@@ -544,7 +544,7 @@ class XinDianTuReadImageKit:
|
||||
|
||||
# 记录上传后的 URL 供识别使用
|
||||
seg["patch_url"] = patch_url
|
||||
tasks.append(XinDianTuReadImageKit.recognize_card_text(patch_url))
|
||||
tasks.append(ReadImageKit.recognize_card_text(patch_url))
|
||||
|
||||
# 等待所有 OCR 完成
|
||||
results = await asyncio.gather(*tasks)
|
||||
@@ -772,7 +772,7 @@ class XinDianTuReadImageKit:
|
||||
loop = asyncio.get_event_loop()
|
||||
resp = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: XinDianTuReadImageKit._client.chat.completions.create(
|
||||
lambda: ReadImageKit._client.chat.completions.create(
|
||||
model=VL_MODEL_NAME,
|
||||
messages=[
|
||||
{
|
||||
@@ -820,7 +820,7 @@ class XinDianTuReadImageKit:
|
||||
"""
|
||||
if device_info is None:
|
||||
logger.warning("未提供动态设备信息,使用通用回退配置。")
|
||||
device_info = XinDianTuReadImageKit._FALLBACK_DEVICE_INFO
|
||||
device_info = ReadImageKit._FALLBACK_DEVICE_INFO
|
||||
|
||||
prompt = (
|
||||
"分析图片中是否存在覆盖在主界面上的广告弹窗(Popup Ad)。"
|
||||
@@ -834,7 +834,7 @@ class XinDianTuReadImageKit:
|
||||
loop = asyncio.get_event_loop()
|
||||
resp = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: XinDianTuReadImageKit._client.chat.completions.create(
|
||||
lambda: ReadImageKit._client.chat.completions.create(
|
||||
model=VL_MODEL_NAME_AD, # 使用更强的视觉模型
|
||||
messages=[
|
||||
{
|
||||
@@ -853,7 +853,7 @@ class XinDianTuReadImageKit:
|
||||
)
|
||||
|
||||
content = resp.choices[0].message.content or ""
|
||||
raw = XinDianTuReadImageKit._extract_json(content)
|
||||
raw = ReadImageKit._extract_json(content)
|
||||
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
@@ -861,7 +861,7 @@ class XinDianTuReadImageKit:
|
||||
if has_ad and data.get("close_button_bounds"):
|
||||
# 使用现有的辅助方法转换坐标
|
||||
temp_obj = {"bounds": data.get("close_button_bounds")}
|
||||
XinDianTuReadImageKit._add_click_point(temp_obj, device_info)
|
||||
ReadImageKit._add_click_point(temp_obj, device_info)
|
||||
return {
|
||||
"has_ad": True,
|
||||
"uia_center_x": temp_obj.get("uia_center_x"),
|
||||
@@ -912,7 +912,7 @@ class XinDianTuReadImageKit:
|
||||
"""
|
||||
if device_info is None:
|
||||
logger.warning("未提供动态设备信息,使用通用回退配置。")
|
||||
device_info = XinDianTuReadImageKit._FALLBACK_DEVICE_INFO
|
||||
device_info = ReadImageKit._FALLBACK_DEVICE_INFO
|
||||
|
||||
# 视觉解析提示词:约束输出字段与格式,避免模型输出说明文字
|
||||
prompt = (
|
||||
@@ -928,7 +928,7 @@ class XinDianTuReadImageKit:
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
def _do_request(url_val):
|
||||
return XinDianTuReadImageKit._client.chat.completions.create(
|
||||
return ReadImageKit._client.chat.completions.create(
|
||||
model=VL_MODEL_NAME,
|
||||
messages=[
|
||||
{
|
||||
@@ -957,7 +957,7 @@ class XinDianTuReadImageKit:
|
||||
# 如果是媒体格式或数据检查错误
|
||||
if 'InvalidParameter.DataInspection' in str(err_code) or 'media format' in str(e).lower():
|
||||
logger.warning(f"Image URL rejected ({err_code}). Attempting Base64 fallback: {image_url}")
|
||||
base64_str = await XinDianTuReadImageKit._download_as_base64(image_url)
|
||||
base64_str = await ReadImageKit._download_as_base64(image_url)
|
||||
if base64_str:
|
||||
# 简单推断格式,默认 jpeg
|
||||
ext = "jpeg"
|
||||
@@ -982,7 +982,7 @@ class XinDianTuReadImageKit:
|
||||
return []
|
||||
|
||||
content = resp.choices[0].message.content or ""
|
||||
raw = XinDianTuReadImageKit._extract_json(content)
|
||||
raw = ReadImageKit._extract_json(content)
|
||||
|
||||
try:
|
||||
# 反序列化模型返回的 JSON
|
||||
@@ -1069,8 +1069,8 @@ class XinDianTuReadImageKit:
|
||||
hourly = []
|
||||
for r in rows:
|
||||
# 起止时间转分钟
|
||||
s = XinDianTuReadImageKit._to_minutes(r.get("start"))
|
||||
e = XinDianTuReadImageKit._to_minutes(r.get("end"))
|
||||
s = ReadImageKit._to_minutes(r.get("start"))
|
||||
e = ReadImageKit._to_minutes(r.get("end"))
|
||||
if e <= s:
|
||||
continue
|
||||
cur = s
|
||||
@@ -1078,8 +1078,8 @@ class XinDianTuReadImageKit:
|
||||
# 下一小时边界;不超过区间终点
|
||||
nxt = min(e, ((cur // 60) + 1) * 60)
|
||||
hourly.append({
|
||||
"start": XinDianTuReadImageKit._fmt(cur),
|
||||
"end": XinDianTuReadImageKit._fmt(nxt),
|
||||
"start": ReadImageKit._fmt(cur),
|
||||
"end": ReadImageKit._fmt(nxt),
|
||||
"price_kwh": r.get("price_kwh"),
|
||||
"electric_fee_kwh": r.get("electric_fee_kwh"),
|
||||
"service_fee_kwh": r.get("service_fee_kwh"),
|
||||
@@ -1120,10 +1120,10 @@ class XinDianTuReadImageKit:
|
||||
"""
|
||||
if device_info is None:
|
||||
logger.warning("未提供动态设备信息,使用通用回退配置。")
|
||||
device_info = XinDianTuReadImageKit._FALLBACK_DEVICE_INFO
|
||||
device_info = ReadImageKit._FALLBACK_DEVICE_INFO
|
||||
|
||||
# 启动寻找“全部时段”按钮的任务,为后续可能的点击做准备
|
||||
button_task = asyncio.create_task(XinDianTuReadImageKit.find_all_time_button_coordinate(image_url, device_info))
|
||||
button_task = asyncio.create_task(ReadImageKit.find_all_time_button_coordinate(image_url, device_info))
|
||||
|
||||
prompt = (
|
||||
"仅输出JSON对象(不含任何说明文字)。"
|
||||
@@ -1139,7 +1139,7 @@ class XinDianTuReadImageKit:
|
||||
loop = asyncio.get_event_loop()
|
||||
resp = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: XinDianTuReadImageKit._client.chat.completions.create(
|
||||
lambda: ReadImageKit._client.chat.completions.create(
|
||||
model=VL_MODEL_NAME,
|
||||
messages=[
|
||||
{
|
||||
@@ -1158,7 +1158,7 @@ class XinDianTuReadImageKit:
|
||||
)
|
||||
|
||||
content = resp.choices[0].message.content or ""
|
||||
raw = XinDianTuReadImageKit._extract_json(content)
|
||||
raw = ReadImageKit._extract_json(content)
|
||||
|
||||
result = {}
|
||||
try:
|
||||
@@ -1200,14 +1200,14 @@ class XinDianTuReadImageKit:
|
||||
# MD5 -> URL 的映射,用于去重
|
||||
md5_to_url = {}
|
||||
for u in image_urls:
|
||||
m = await XinDianTuReadImageKit._fetch_md5(u)
|
||||
m = await ReadImageKit._fetch_md5(u)
|
||||
if m and m not in md5_to_url:
|
||||
md5_to_url[m] = u
|
||||
unique_urls = list(md5_to_url.values())
|
||||
# 合并时段结果
|
||||
combined = []
|
||||
for u in unique_urls:
|
||||
rows = await XinDianTuReadImageKit.parse_price_schedule(station_name, u, device_info=device_info)
|
||||
rows = await ReadImageKit.parse_price_schedule(station_name, u, device_info=device_info)
|
||||
if rows:
|
||||
combined.extend(rows)
|
||||
return combined
|
||||
@@ -1230,8 +1230,8 @@ class XinDianTuReadImageKit:
|
||||
# 预处理:转换为分钟区间
|
||||
intervals = []
|
||||
for r in rows:
|
||||
s = XinDianTuReadImageKit._to_minutes(r.get("start"))
|
||||
e = XinDianTuReadImageKit._to_minutes(r.get("end"))
|
||||
s = ReadImageKit._to_minutes(r.get("start"))
|
||||
e = ReadImageKit._to_minutes(r.get("end"))
|
||||
if e <= s:
|
||||
continue
|
||||
s = max(0, s)
|
||||
@@ -1256,8 +1256,8 @@ class XinDianTuReadImageKit:
|
||||
best_overlap = overlap
|
||||
best = it
|
||||
result.append({
|
||||
"start": XinDianTuReadImageKit._fmt(hs),
|
||||
"end": XinDianTuReadImageKit._fmt(he),
|
||||
"start": ReadImageKit._fmt(hs),
|
||||
"end": ReadImageKit._fmt(he),
|
||||
"price_kwh": best["price_kwh"] if best else None,
|
||||
"electric_fee_kwh": best["electric_fee_kwh"] if best else None,
|
||||
"service_fee_kwh": best["service_fee_kwh"] if best else None,
|
||||
@@ -1269,7 +1269,7 @@ async def test1():
|
||||
# 测试新电途第一层结构的读取
|
||||
url = "https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/Temp/8fd79c68-fec6-4ca7-8d8e-fbff3c6862c8.jpg"
|
||||
print(f"Testing First Level with URL: {url}")
|
||||
result = await XinDianTuReadImageKit.parse_first_level_image_url(url)
|
||||
result = await ReadImageKit.parse_first_level_image_url(url)
|
||||
print("First Level Result:")
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
|
||||
@@ -1278,7 +1278,7 @@ async def test2(station_name: str):
|
||||
# 测试新电途第二层结构的读取
|
||||
url = "https://dsideal.obs.myhuaweicloud.com/HuangHai/Temp/SecondPage.jpg"
|
||||
print(f"Testing address extraction from: {url}")
|
||||
result = await XinDianTuReadImageKit.parse_address(station_name, url)
|
||||
result = await ReadImageKit.parse_address(station_name, url)
|
||||
result["station_name"] = station_name
|
||||
print("Address result:")
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
@@ -1290,8 +1290,8 @@ async def test3(station_name: str):
|
||||
"https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/Temp/XinDianTu/1.jpg",
|
||||
"https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/Temp/XinDianTu/2.jpg",
|
||||
]
|
||||
rows = await XinDianTuReadImageKit.parse_price_schedule_multi(station_name, samples)
|
||||
hourly = XinDianTuReadImageKit.hourly_full_day(rows)
|
||||
rows = await ReadImageKit.parse_price_schedule_multi(station_name, samples)
|
||||
hourly = ReadImageKit.hourly_full_day(rows)
|
||||
print(json.dumps(hourly, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(_
|
||||
if project_root not in sys.path:
|
||||
sys.path.append(project_root)
|
||||
|
||||
from Apps.XinDianTu.XinDianTuReadImageKit import XinDianTuReadImageKit
|
||||
from Apps.XinDianTu.ReadImageKit import ReadImageKit
|
||||
from DbKit.Db import Db
|
||||
from Config.Config import DB_URL
|
||||
from Model.StationProfile import StationProfile
|
||||
@@ -46,7 +46,7 @@ class XinDianTuService:
|
||||
"""
|
||||
基于 VL 模式处理场站列表
|
||||
"""
|
||||
station_list = await XinDianTuReadImageKit.parse_vl_image(vl_image_url, json_metadata, device_info=device_info)
|
||||
station_list = await ReadImageKit.parse_vl_image(vl_image_url, json_metadata, device_info=device_info)
|
||||
if not station_list:
|
||||
return []
|
||||
|
||||
@@ -163,7 +163,7 @@ class XinDianTuService:
|
||||
"""
|
||||
混合模式处理场站列表 (图形学切片 + OCR)
|
||||
"""
|
||||
station_list = await XinDianTuReadImageKit.parse_hybrid_image(local_image_path, uploader, cdn_domain)
|
||||
station_list = await ReadImageKit.parse_hybrid_image(local_image_path, uploader, cdn_domain)
|
||||
if not station_list:
|
||||
return []
|
||||
|
||||
@@ -241,7 +241,7 @@ class XinDianTuService:
|
||||
return processed_stations
|
||||
|
||||
async def process_station_list(self, list_url: str, device_info: dict = None) -> list:
|
||||
station_list = await XinDianTuReadImageKit.parse_first_level_image_url(list_url, device_info=device_info)
|
||||
station_list = await ReadImageKit.parse_first_level_image_url(list_url, device_info=device_info)
|
||||
if not station_list:
|
||||
return []
|
||||
|
||||
@@ -338,7 +338,7 @@ class XinDianTuService:
|
||||
async def process_station_address(self, station_name: str, detail_url: str, device_info: dict = None) -> dict:
|
||||
logger.info(f"\n[Service] Processing Address for '{station_name}' from: {detail_url}")
|
||||
|
||||
address_data = await XinDianTuReadImageKit.parse_address(station_name, detail_url, device_info=device_info)
|
||||
address_data = await ReadImageKit.parse_address(station_name, detail_url, device_info=device_info)
|
||||
address = address_data.get("address")
|
||||
if address:
|
||||
address = address.strip()
|
||||
@@ -493,8 +493,8 @@ class XinDianTuService:
|
||||
async def process_price_schedule(self, station_name: str, schedule_urls: list, device_info: dict = None):
|
||||
logger.info(f"\n[Service] Processing Price Schedule for '{station_name}'")
|
||||
|
||||
rows = await XinDianTuReadImageKit.parse_price_schedule_multi(station_name, schedule_urls, device_info=device_info)
|
||||
hourly_schedule = XinDianTuReadImageKit.hourly_full_day(rows)
|
||||
rows = await ReadImageKit.parse_price_schedule_multi(station_name, schedule_urls, device_info=device_info)
|
||||
hourly_schedule = ReadImageKit.hourly_full_day(rows)
|
||||
|
||||
if hourly_schedule:
|
||||
logger.info(f" - Got {len(hourly_schedule)} hourly records.")
|
||||
|
||||
BIN
Apps/XinDianTu/__pycache__/ReadImageKit.cpython-310.pyc
Normal file
BIN
Apps/XinDianTu/__pycache__/ReadImageKit.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user