refactor: Migrate image generation to async Playwright

This commit is contained in:
Ching L 2025-03-05 23:01:00 +08:00
parent 9c8b360553
commit f18f03a7f7
2 changed files with 49 additions and 34 deletions

13
dota.py
View File

@ -6,6 +6,7 @@ import json
import players import players
import utils import utils
from image_generator import ImageGenerator from image_generator import ImageGenerator
import asyncio
db = peewee.SqliteDatabase('dota.db') db = peewee.SqliteDatabase('dota.db')
hero_client = opendota.HeroesApi() hero_client = opendota.HeroesApi()
@ -178,11 +179,12 @@ class Friend(BaseModel):
}) })
# 生成图片报告 # 生成图片报告
image_url = None
try: try:
image_url = image_generator.generate_recent_matches_image(name, matches[:limit]) # 使用asyncio运行异步函数
image_url = asyncio.run(image_generator.generate_recent_matches_image(name, matches[:limit]))
except Exception as e: except Exception as e:
logger.error(f"生成最近比赛报告图片失败: {str(e)}") logger.error(f"生成最近比赛报告图片失败: {str(e)}")
image_url = None
# 如果成功生成了图片添加到第一个embed中 # 如果成功生成了图片添加到第一个embed中
if image_url: if image_url:
@ -319,11 +321,12 @@ def serialize_match_for_discord(match_):
dire_indicator = ' 🌟' dire_indicator = ' 🌟'
# 生成比赛报告图片 # 生成比赛报告图片
image_url = None
try: try:
image_url = image_generator.generate_match_report(match_) # 使用asyncio运行异步函数
image_url = asyncio.run(image_generator.generate_match_report(match_))
except Exception as e: except Exception as e:
logger.error(f"生成比赛报告图片失败: {str(e)}") logger.error(f"生成比赛报告图片失败: {str(e)}")
image_url = None
data = { data = {
"content": content, "content": content,

View File

@ -1,9 +1,10 @@
from playwright.sync_api import sync_playwright from playwright.async_api import async_playwright
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
import utils import utils
import json import json
import os import os
import datetime import datetime
from loguru import logger
class ImageGenerator: class ImageGenerator:
@ -20,7 +21,7 @@ class ImageGenerator:
return f"https://cdn.cloudflare.steamstatic.com{hero_info['img']}" return f"https://cdn.cloudflare.steamstatic.com{hero_info['img']}"
return None return None
def generate_match_report(self, match_data): async def generate_match_report(self, match_data):
""" """
根据match_data生成比赛报告图片 根据match_data生成比赛报告图片
match_data应该是dota.py中serialize_match方法返回的格式 match_data应该是dota.py中serialize_match方法返回的格式
@ -112,30 +113,38 @@ class ImageGenerator:
html_content = template.render(**template_data) html_content = template.render(**template_data)
# 使用Playwright生成图片 # 使用Playwright生成图片
with sync_playwright() as playwright: async with async_playwright() as playwright:
browser = playwright.chromium.launch() browser = await playwright.chromium.launch()
page = browser.new_page() page = await browser.new_page()
page.set_content(html_content) await page.set_content(html_content)
page.set_viewport_size({"width": 800, "height": 800}) await page.set_viewport_size({"width": 800, "height": 800})
# 等待内容完全加载 # 等待内容完全加载
page.wait_for_timeout(1000) await page.wait_for_timeout(1000)
# 调整截图高度以适应内容 # 调整截图高度以适应内容
body_height = page.evaluate('document.body.scrollHeight') body_height = await page.evaluate('document.body.scrollHeight')
body_width = page.evaluate('document.body.offsetWidth') body_width = await page.evaluate('document.body.offsetWidth')
page.set_viewport_size({"width": body_width, "height": body_height}) await page.set_viewport_size({"width": body_width, "height": body_height})
# 截图 # 截图
image_path = "match_report_%s.png" % match_data.get('match_id') image_path = f"match_report_{match_data.get('match_id')}.png"
page.screenshot(path=image_path, full_page=True) await page.screenshot(path=image_path, full_page=True)
browser.close() await browser.close()
image_url = utils.upload_image(image_path, image_path)
os.remove(image_path) # 上传图片
image_url = utils.upload_image(image_path)
# 删除本地文件
try:
os.remove(image_path)
except Exception as e:
logger.warning(f"删除本地图片文件失败: {str(e)}")
return image_url return image_url
return None return None
def generate_recent_matches_image(self, player_name, matches): async def generate_recent_matches_image(self, player_name, matches):
""" """
生成玩家最近比赛的图片报告 生成玩家最近比赛的图片报告
@ -187,30 +196,33 @@ class ImageGenerator:
) )
# 使用Playwright生成图片 # 使用Playwright生成图片
with sync_playwright() as playwright: async with async_playwright() as playwright:
browser = playwright.chromium.launch() browser = await playwright.chromium.launch()
page = browser.new_page() page = await browser.new_page()
page.set_content(html_content) await page.set_content(html_content)
page.set_viewport_size({"width": 800, "height": 800}) await page.set_viewport_size({"width": 800, "height": 800})
# 等待内容完全加载 # 等待内容完全加载
page.wait_for_timeout(1000) await page.wait_for_timeout(1000)
# 调整截图高度以适应内容 # 调整截图高度以适应内容
body_height = page.evaluate('document.body.scrollHeight') body_height = await page.evaluate('document.body.scrollHeight')
body_width = page.evaluate('document.body.offsetWidth') body_width = await page.evaluate('document.body.offsetWidth')
page.set_viewport_size({"width": body_width, "height": body_height}) await page.set_viewport_size({"width": body_width, "height": body_height})
# 截图 # 截图
image_path = f"recent_matches_{player_name.replace(' ', '_')}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.png" image_path = f"recent_matches_{player_name.replace(' ', '_')}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.png"
page.screenshot(path=image_path, full_page=True) await page.screenshot(path=image_path, full_page=True)
browser.close() await browser.close()
# 上传图片 # 上传图片
image_url = utils.upload_image(image_path) image_url = utils.upload_image(image_path)
# 删除本地文件 # 删除本地文件
os.remove(image_path) try:
os.remove(image_path)
except Exception as e:
logger.warning(f"删除本地图片文件失败: {str(e)}")
return image_url return image_url