| | |
| | | from docx import Document |
| | | from docx.shared import Inches |
| | | from docx.enum.text import WD_ALIGN_PARAGRAPH |
| | | from docx.shared import Pt, Inches |
| | | from docx.oxml.ns import qn |
| | | from io import BytesIO |
| | | from docx.enum.text import WD_ALIGN_PARAGRAPH |
| | | import random |
| | | import threading |
| | | import re |
| | |
| | | from io import BytesIO |
| | | from PIL import Image |
| | | import google.generativeai as genai |
| | | from auto_media_publisher.config.conf_base import GOOGLE_API_KEY,GOOGLE_MODEL,GOOGLE_CHAT_MIN_INTERVAL |
| | | from auto_media_publisher.config.conf_base import GOOGLE_GEMINI_TEMPERATURE_MIN,GOOGLE_GEMINI_TEMPERATURE_MAX,GOOGLE_REWRITE_STYLE_MAP,GEMINI_PROMPT_INIT_DEFAULT |
| | | from auto_media_publisher.config.conf_base import GOOGLE_API_KEY,GOOGLE_MODEL,GOOGLE_CHAT_MIN_INTERVAL,DEFAULT_PREFIX_MAP,DEFAULT_SURFIX_MAP |
| | | from auto_media_publisher.config.conf_base import GOOGLE_GEMINI_TEMPERATURE_MIN,GOOGLE_GEMINI_TEMPERATURE_MAX,GOOGLE_GEMINI_TOPN_MIN,GOOGLE_GEMINI_TOPN_MAX,GOOGLE_REWRITE_STYLE_MAP,GEMINI_PROMPT_INIT_DEFAULT |
| | | from auto_media_publisher.config.conf_base import GEMINI_PROMPT_TITLE,GEMINI_PROMPT_DESCRIPTION,GEMINI_PROMPT_TAG |
| | | from auto_media_publisher.utils.logger import get_logger |
| | | from auto_media_publisher.utils.utils_text2image_pollinations import Text2ImageGenerator |
| | | |
| | | class GeminiDocxRewriter: |
| | | def __init__(self,rewrite_style="default" ): |
| | | |
| | | class DocxRewriterGemini: |
| | | def __init__(self,rewrite_style="default" ,docx_author="我"): |
| | | genai.configure(api_key=GOOGLE_API_KEY) |
| | | self.temperature = round(random.uniform(GOOGLE_GEMINI_TEMPERATURE_MIN, GOOGLE_GEMINI_TEMPERATURE_MAX), 2) |
| | | self.top_n = round(random.uniform(GOOGLE_GEMINI_TOPN_MIN, GOOGLE_GEMINI_TOPN_MAX), 2) |
| | | self.model = genai.GenerativeModel(GOOGLE_MODEL, |
| | | generation_config=genai.types.GenerationConfig( |
| | | temperature=self.temperature, |
| | | top_p=1.0 |
| | | top_p=self.top_n, |
| | | top_k=random.choice([40, 50, 80, 100]) |
| | | ) |
| | | ) |
| | | if rewrite_style =="random": |
| | |
| | | else: |
| | | self.rewrite_style = rewrite_style |
| | | |
| | | self.prefix_text = f"我是{docx_author},{random.choice(DEFAULT_PREFIX_MAP)}" |
| | | self.surfix_text = random.choice(DEFAULT_SURFIX_MAP) |
| | | |
| | | self.last_call_time = 0 |
| | | self.min_interval = GOOGLE_CHAT_MIN_INTERVAL # 限制最短请求间隔(秒) |
| | | self.lock = threading.Lock() # 多线程并发时也安全 |
| | | self.logger = get_logger(self.__class__.__name__,"admin") |
| | | self.text2imager = Text2ImageGenerator() |
| | | self.text2imager = Text2ImageGenerator(width=900,height=383) |
| | | |
| | | self.summary_info = {} |
| | | |
| | | #@retry(stop=stop_after_attempt(3), wait=wait_fixed(2)) # 自动重试机制 |
| | | def safe_send_message(self, content: str): |
| | |
| | | rewritten = self._rewrite_blocks(blocks) |
| | | self._save_to_new_docx(rewritten, output_path) |
| | | |
| | | summary_info = self.summarize_title_intro_keywords() |
| | | print(summary_info) |
| | | self.text2imager.generate_image(summary_info["summary"],output_path.replace(".docx",".jpg")) |
| | | summary_info["cover_image"] = output_path.replace(".docx",".jpg") |
| | | self.summary_info = self._summarize_title_intro_keywords() |
| | | print(self.summary_info) |
| | | self.text2imager.generate_image(self.summary_info["description"],output_path.replace(".docx",".png")) |
| | | self.summary_info["path_cover"] = output_path.replace(".docx",".png") |
| | | |
| | | # 3. 写入磁盘 |
| | | with open(output_path.replace(".docx",".json"), "w", encoding="utf-8") as f: |
| | | json.dump(summary_info, f, ensure_ascii=False, indent=4) |
| | | |
| | | return summary_info |
| | | |
| | | def extract_docx_blocks_by_order(self,docx_path): |
| | | doc = Document(docx_path) |
| | |
| | | |
| | | def _save_to_new_docx(self, blocks, output_path): |
| | | doc = Document() |
| | | # 设置默认样式 |
| | | style = doc.styles['Normal'] |
| | | font = style.font |
| | | font.name = '楷体' # 西文字体设置为楷体 |
| | | font.size = Pt(10.5) # 小四字体 |
| | | style._element.rPr.rFonts.set(qn('w:eastAsia'), '楷体') # 中文字体设定 |
| | | |
| | | # 前言 |
| | | para = doc.add_paragraph(self.prefix_text) |
| | | para.paragraph_format.line_spacing = 1.5 |
| | | para.paragraph_format.space_before = Pt(0) |
| | | para.paragraph_format.space_after = Pt(0) |
| | | |
| | | |
| | | for block in blocks: |
| | | if block["type"]=="text": |
| | | if block["type"] == "text": |
| | | for line in block["content"].split("\n"): |
| | | doc.add_paragraph(line.strip()) |
| | | para = doc.add_paragraph(line.strip()) |
| | | para.paragraph_format.line_spacing = 1.5 # 行距1.5倍 |
| | | para.paragraph_format.space_before = Pt(0) # 段前0磅 |
| | | para.paragraph_format.space_after = Pt(0) # 段后0磅 |
| | | elif block["type"] == "image": |
| | | image_data = block["content"] |
| | | image_stream = BytesIO(image_data) |
| | | paragraph = doc.add_paragraph() |
| | | run = paragraph.add_run() |
| | | run.add_picture(image_stream, width=Inches(5.5)) # 适当调整宽度 |
| | | run.add_picture(image_stream, width=Inches(5.5)) |
| | | paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER |
| | | |
| | | doc.save(output_path) |
| | | |
| | | # 结语 |
| | | para2 = doc.add_paragraph(self.surfix_text) |
| | | para2.paragraph_format.line_spacing = 1.5 |
| | | para2.paragraph_format.space_before = Pt(12) |
| | | para2.paragraph_format.space_after = Pt(0) |
| | | |
| | | def summarize_title_intro_keywords(self) -> dict: |
| | | doc.save(output_path) |
| | | |
| | | |
| | | def _summarize_title_intro_keywords(self) -> dict: |
| | | title = self.safe_send_message(GEMINI_PROMPT_TITLE).text.strip() |
| | | summary = self.safe_send_message(GEMINI_PROMPT_DESCRIPTION).text.strip() |
| | | description = self.safe_send_message(GEMINI_PROMPT_DESCRIPTION).text.strip() |
| | | keywords = self.safe_send_message(GEMINI_PROMPT_TAG).text.strip() |
| | | |
| | | return { |
| | | "title": title, |
| | | "summary": summary, |
| | | "keywords": re.split(r'[、,\s]+', keywords.strip("。")) |
| | | "title": title.splitlines()[0].strip()[:64], |
| | | "description": description[:120], |
| | | "tags": re.split(r'[、,\s]+', keywords.strip("。")) |
| | | } |
| | | |