Files
archived-MoviePilot-Plugins/plugins/autosubv2/translate/openai_translate.py
2026-01-21 17:30:59 +08:00

148 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import time
import random
from typing import List, Union
import openai
from cacheout import Cache
OpenAISessionCache = Cache(maxsize=100, ttl=3600, timer=time.time, default=None)
class OpenAi:
_api_key: str = None
_api_url: str = None
_model: str = "gpt-3.5-turbo"
def __init__(self, api_key: str = None, api_url: str = None, proxy: dict = None, model: str = None,
compatible: bool = False):
self._api_key = api_key
self._api_url = api_url
openai.api_base = self._api_url if compatible else self._api_url + "/v1"
openai.api_key = self._api_key
if proxy and proxy.get("https"):
openai.proxy = proxy.get("https")
if model:
self._model = model
@staticmethod
def __save_session(session_id: str, message: str):
"""
保存会话
:param session_id: 会话ID
:param message: 消息
:return:
"""
seasion = OpenAISessionCache.get(session_id)
if seasion:
seasion.append({
"role": "assistant",
"content": message
})
OpenAISessionCache.set(session_id, seasion)
@staticmethod
def __get_session(session_id: str, message: str) -> List[dict]:
"""
获取会话
:param session_id: 会话ID
:return: 会话上下文
"""
seasion = OpenAISessionCache.get(session_id)
if seasion:
seasion.append({
"role": "user",
"content": message
})
else:
seasion = [
{
"role": "system",
"content": "请在接下来的对话中请使用中文回复,并且内容尽可能详细。"
},
{
"role": "user",
"content": message
}]
OpenAISessionCache.set(session_id, seasion)
return seasion
def __get_model(self, message: Union[str, List[dict]],
prompt: str = None,
user: str = "MoviePilot",
**kwargs):
"""
获取模型
"""
if not isinstance(message, list):
if prompt:
message = [
{
"role": "system",
"content": prompt
},
{
"role": "user",
"content": message
}
]
else:
message = [
{
"role": "user",
"content": message
}
]
return self.client.chat.completions.create(
model=self._model,
user=user,
messages=message,
**kwargs
)
@staticmethod
def __clear_session(session_id: str):
"""
清除会话
:param session_id: 会话ID
:return:
"""
if OpenAISessionCache.get(session_id):
OpenAISessionCache.delete(session_id)
def translate_to_zh(self, text: str, context: str = None, max_retries: int = 3):
"""
翻译为中文
:param text: 输入文本
:param context: 翻译上下文
:param max_retries: 最大重试次数
"""
system_prompt = """您是一位专业字幕翻译专家,请严格遵循以下规则:
1. 将原文精准翻译为简体中文,保持原文本意
2. 使用自然的口语化表达,符合中文观影习惯
3. 结合上下文语境,人物称谓、专业术语、情感语气在上下文中保持连贯
4. 按行翻译待译内容。翻译结果不要包括上下文。
5. 输出内容必须仅包括译文。不要输出任何开场白,解释说明或总结"""
user_prompt = f"翻译上下文:\n{context}\n\n需要翻译的内容:\n{text}" if context else f"请翻译:\n{text}"
last_error = ""
for attempt in range(max_retries + 1):
try:
completion = self.__get_model(prompt=system_prompt,
message=user_prompt,
temperature=0.2,
top_p=0.9)
result = completion.choices[0].message.content.strip()
return True, result
except Exception as e:
last_error = str(e)
if attempt < max_retries:
# 使用指数退避和随机抖动,避免多个请求同时重试
base_delay = 2 ** attempt # 指数退避: 1s, 2s, 4s...
jitter = random.uniform(0.1, 0.9) # 随机抖动: 0.1-0.9秒
sleep_time = base_delay + jitter
print(f"翻译请求失败 (第{attempt + 1}次尝试){last_error}{sleep_time:.1f}秒后重试...")
time.sleep(sleep_time)
else:
print(f"翻译请求失败 (已重试{max_retries}次){last_error}")
return False, f"{last_error}"