mirror of
https://github.com/Murasame-Dev/McStatus-Img.git
synced 2025-12-15 20:57:54 +00:00
Upgrade
This commit is contained in:
parent
5c576c811f
commit
57a741ca17
9 changed files with 199 additions and 18 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
*.png
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "api"]
|
||||||
|
path = mc_status_api
|
||||||
|
url = https://github.com/Murasame-Dev/McStatus-API.git
|
||||||
BIN
MiSans-Bold.ttf
Normal file
BIN
MiSans-Bold.ttf
Normal file
Binary file not shown.
76
create_image.py
Normal file
76
create_image.py
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||||
|
from io import BytesIO
|
||||||
|
from motd_formatter import foramt_motd
|
||||||
|
from PIL.ImageColor import getrgb
|
||||||
|
|
||||||
|
def create_background(input: bytes, width: int, height: int):
|
||||||
|
background = Image.open(BytesIO(input))
|
||||||
|
w1, h1, w2, h2 = background.getbbox()
|
||||||
|
midw = (w1 + w2) // 2
|
||||||
|
midh = (h1 + h2) // 2
|
||||||
|
rheight = int(height / width * w2)
|
||||||
|
rwidth = w2
|
||||||
|
if rheight > h2:
|
||||||
|
rwidth = int(width / height * h2)
|
||||||
|
rheight = h2
|
||||||
|
|
||||||
|
background = background.crop((midw - int(rwidth / 2), midh - int(rheight / 2), midw + int(rwidth / 2), midh + int(rheight / 2)))
|
||||||
|
background = background.resize((width, height), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
|
blurred_background = background.filter(ImageFilter.GaussianBlur(radius=5))
|
||||||
|
|
||||||
|
image = blurred_background
|
||||||
|
return image
|
||||||
|
|
||||||
|
def draw_text_with_shadow(image: Image.Image, text: str, posx: int, posy: int):
|
||||||
|
font_size = 14
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
font = ImageFont.truetype("./MiSans-Bold.ttf", font_size)
|
||||||
|
draw.text((posx + 2, posy + 2), text, font=font, fill='black')
|
||||||
|
draw.text((posx, posy), text, font=font, fill='white')
|
||||||
|
|
||||||
|
def draw_motd_text_with_shadow(image: Image.Image, text: str, posx: int, posy: int):
|
||||||
|
font_size = 10
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
font = ImageFont.truetype("./MiSans-Bold.ttf", font_size)
|
||||||
|
w1, _, w2, _ = draw.textbbox((0, 0), text, font=font)
|
||||||
|
weight = w2 - w1
|
||||||
|
motd_list = foramt_motd(text, weight)
|
||||||
|
for pos, color, text in motd_list:
|
||||||
|
draw.text((posx + pos + 1, posy + 1), text, font=font, fill='black')
|
||||||
|
draw.text((posx + pos, posy), text, font=font, fill=getrgb(color))
|
||||||
|
|
||||||
|
def create_image(background: bytes, icon: str | None, text_list: list[str], motd_list: list[str]):
|
||||||
|
# 图片尺寸
|
||||||
|
width, height = 600, 200
|
||||||
|
if (len(text_list) + len(motd_list)) * 20 + 20 > height:
|
||||||
|
height = len(text_list) * 20 + 20
|
||||||
|
|
||||||
|
try:
|
||||||
|
image = create_background(background, width, height)
|
||||||
|
except FileNotFoundError:
|
||||||
|
image = Image.new('RGB', (width, height), color='orange')
|
||||||
|
|
||||||
|
# 添加半透明蒙版层以增强文字可读性
|
||||||
|
overlay = Image.new('RGBA', (width, height), (0, 0, 0, 80)) # 半透明黑色蒙版
|
||||||
|
image.paste(overlay, (0, 0), overlay)
|
||||||
|
|
||||||
|
if width // 2 > height:
|
||||||
|
small_size = int(height * 0.8)
|
||||||
|
else:
|
||||||
|
small_size = width // 3
|
||||||
|
if icon == None:
|
||||||
|
small_image = Image.new('RGBA', (small_size, small_size), color='gray')
|
||||||
|
else:
|
||||||
|
small_image = Image.open(BytesIO(icon)).resize((small_size, small_size), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
|
image.paste(small_image, (30, height // 2 - small_size // 2))
|
||||||
|
|
||||||
|
text_list_size = len(text_list)
|
||||||
|
motd_list_size = len(motd_list)
|
||||||
|
for i in range(text_list_size):
|
||||||
|
draw_text_with_shadow(image, text_list[i], width // 2.5, 10 + 20 * i)
|
||||||
|
for i in range(motd_list_size):
|
||||||
|
draw_motd_text_with_shadow(image, motd_list[i], width // 2.5, 10 + 20 * (i + text_list_size))
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import httpx
|
import httpx
|
||||||
import os
|
|
||||||
|
|
||||||
def download_image_with_httpx_auto_redirect(url, save_path):
|
def download_image_with_httpx_auto_redirect(url:str):
|
||||||
"""
|
"""
|
||||||
使用httpx库自动处理重定向下载图片
|
使用httpx库自动处理重定向下载图片
|
||||||
|
|
||||||
|
|
@ -16,17 +15,7 @@ def download_image_with_httpx_auto_redirect(url, save_path):
|
||||||
|
|
||||||
# 检查状态码
|
# 检查状态码
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
# 确保保存目录存在
|
|
||||||
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
|
||||||
|
|
||||||
# 写入文件
|
|
||||||
with open(save_path, 'wb') as f:
|
|
||||||
f.write(response.content)
|
|
||||||
|
|
||||||
Background = response.content
|
Background = response.content
|
||||||
|
|
||||||
print(f"图片已成功保存到: {save_path}")
|
|
||||||
print(f"最终URL: {response.url}") # 显示最终重定向后的URL
|
|
||||||
return Background
|
return Background
|
||||||
else:
|
else:
|
||||||
print(f"请求失败,状态码: {response.status_code}")
|
print(f"请求失败,状态码: {response.status_code}")
|
||||||
|
|
@ -35,9 +24,3 @@ def download_image_with_httpx_auto_redirect(url, save_path):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"下载失败: {e}")
|
print(f"下载失败: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
if __name__ == "__main__":
|
|
||||||
image_url = "https://www.loliapi.com/acg/"
|
|
||||||
save_path = "images/downloaded_image.jpg"
|
|
||||||
download_image_with_httpx_auto_redirect(image_url, save_path)
|
|
||||||
44
main.py
Normal file
44
main.py
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
from get_background import download_image_with_httpx_auto_redirect
|
||||||
|
from create_image import create_image
|
||||||
|
|
||||||
|
# Java版查询模块
|
||||||
|
from mc_status_api.JavaServerStatus import java_status
|
||||||
|
# 基岩版查询模块
|
||||||
|
from mc_status_api.BedrockServerStatus import bedrock_status
|
||||||
|
# 此API优先解析 srv 记录
|
||||||
|
from mc_status_api.dnslookup import dns_lookup
|
||||||
|
# 格式化文本
|
||||||
|
from mc_status_api.FormatData import format_java_data, format_bedrock_data, format_index, format_java_index, format_bedrock_index
|
||||||
|
|
||||||
|
import base64
|
||||||
|
|
||||||
|
BACKGROUND_URL = "https://www.loliapi.com/acg/"
|
||||||
|
|
||||||
|
def generate_java_status_image(addr: str, output_image_path: str):
|
||||||
|
try:
|
||||||
|
ip, type = dns_lookup(addr)
|
||||||
|
status = java_status(ip)
|
||||||
|
data = format_java_data(ip, type, status)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"查询服务器时出错: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
background_data = download_image_with_httpx_auto_redirect(BACKGROUND_URL)
|
||||||
|
if not background_data:
|
||||||
|
background_data = None
|
||||||
|
|
||||||
|
motd_list = data['motd'].split("\n")
|
||||||
|
text_list = [
|
||||||
|
f"ip: {data["ip"]}",
|
||||||
|
f"type: {data['type']}",
|
||||||
|
f"version: {data['version']}",
|
||||||
|
f"latency: {data['latency']} ms",
|
||||||
|
f"players: {data['players']['online']}/{data['players']['max']}",
|
||||||
|
]
|
||||||
|
|
||||||
|
image = create_image(background_data, base64.b64decode(status.icon.split(",")[1]), text_list, motd_list)
|
||||||
|
image.save(output_image_path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_java_status_image("mc.hypixel.net", "output_image.png")
|
||||||
1
mc_status_api
Submodule
1
mc_status_api
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 7346b1f7729c7d0884f5ba705420f8a6aeb643b8
|
||||||
69
motd_formatter.py
Normal file
69
motd_formatter.py
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
def format_color(color_code: str) -> str:
|
||||||
|
"""
|
||||||
|
将Minecraft颜色代码转换为对应的颜色名称
|
||||||
|
|
||||||
|
Args:
|
||||||
|
color_code (str): Minecraft颜色代码(单个字符)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 对应的颜色名称
|
||||||
|
"""
|
||||||
|
color_map = {
|
||||||
|
'0': '#000000',
|
||||||
|
'1': '#0000AA',
|
||||||
|
'2': '#00AA00',
|
||||||
|
'3': '#00AAAA',
|
||||||
|
'4': '#AA0000',
|
||||||
|
'5': '#AA00AA',
|
||||||
|
'6': '#FFAA00',
|
||||||
|
'7': '#AAAAAA',
|
||||||
|
'8': '#555555',
|
||||||
|
'9': '#5555FF',
|
||||||
|
'a': '#55FF55',
|
||||||
|
'b': '#55FFFF',
|
||||||
|
'c': '#FF5555',
|
||||||
|
'd': '#FF55FF',
|
||||||
|
'e': '#FFFF55',
|
||||||
|
'f': '#FFFFFF',
|
||||||
|
'g': '#DDD605',
|
||||||
|
'r': '#FFFFFF'
|
||||||
|
}
|
||||||
|
return color_map.get(color_code.lower(), 'white')
|
||||||
|
|
||||||
|
def foramt_motd(data: str, weight: int) -> list[tuple[int, str, str]]:
|
||||||
|
"""
|
||||||
|
格式化 MOTD 文本,去除多余的空格和换行符
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (str): 原始 MOTD 文本
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: 格式化后的 MOTD 文本
|
||||||
|
"""
|
||||||
|
iter = 0
|
||||||
|
character_count = 0
|
||||||
|
motd_list = []
|
||||||
|
color_state = "#FFFFFF"
|
||||||
|
data_size = len(data)
|
||||||
|
while iter < data_size:
|
||||||
|
if data[iter] == "§":
|
||||||
|
if iter + 1 < data_size:
|
||||||
|
color = data[iter + 1]
|
||||||
|
text = ""
|
||||||
|
iter += 2
|
||||||
|
character_count += 1
|
||||||
|
while iter < data_size and data[iter] != "§" and data[iter] != "\n":
|
||||||
|
text += data[iter]
|
||||||
|
iter += 1
|
||||||
|
if color != "l":
|
||||||
|
color_state = format_color(color)
|
||||||
|
motd_list.append(((iter - character_count * 2) / data_size * weight, color_state, text))
|
||||||
|
else:
|
||||||
|
iter += 1
|
||||||
|
else:
|
||||||
|
text = ""
|
||||||
|
while iter < data_size and data[iter] != "§" and data[iter] != "\n":
|
||||||
|
text += data[iter]
|
||||||
|
iter += 1
|
||||||
|
motd_list.append((len(text), "white", text))
|
||||||
|
return motd_list
|
||||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pillow
|
||||||
|
mcstatus >= 12.0.5
|
||||||
Loading…
Add table
Add a link
Reference in a new issue