chenpei d0dc94e36d feat: 将弹幕功能替换为AI聊天助手
新功能:
- 创建AI聊天助手组件(AIChatWidget)
- 支持文字+图片多模态对话
- 集成Gemini AI API
- 支持图片编辑和内容生成
- 实时对话历史管理
- 优雅的UI设计,支持黑夜模式

技术实现:
- 后端API代理Gemini API(/api/ai-chat)
- 支持最多5张图片上传
- 流式响应处理
- 对话历史管理
- Base64图片处理

配置:
- 在.env中添加Gemini API配置
- GEMINI_API_KEY
- GEMINI_API_BASE_URL
- GEMINI_MODEL

替换原弹幕功能,提供更强大的AI助手体验!
2025-10-27 17:45:37 +08:00

379 lines
15 KiB
Python
Executable File

# coding:utf-8
import json
import os
from volcengine.ApiInfo import ApiInfo
from volcengine.Credentials import Credentials
from volcengine.ServiceInfo import ServiceInfo
from volcengine.base.Service import Service
from volcengine.const.Const import *
from volcengine.Policy import *
import aiohttp
from volcengine.auth.SignerV4 import SignerV4
LIVESAAS_SERVICE_NAME = "livesaas"
LIVESAAS_API_VERSION = "2020-06-01"
service_info_map = {
REGION_CN_NORTH1: ServiceInfo(
"livesaas.volcengineapi.com",
{'Accept': 'application/json'},
Credentials('', '', LIVESAAS_SERVICE_NAME, REGION_CN_NORTH1),
5, 5, "https"),
}
api_info = {
# 直播间管理类接口
"CreateActivityAPI":
ApiInfo("POST", "/", {"Action": "CreateActivityAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"DeleteActivityAPI":
ApiInfo("POST", "/", {"Action": "DeleteActivityAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"ListActivityAPI":
ApiInfo("GET", "/", {"Action": "ListActivityAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
# 直播控制类接口
"UpdateActivityStatusAPI":
ApiInfo("POST", "/", {"Action": "UpdateActivityStatusAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"UpdatePullToPushAPI":
ApiInfo("POST", "/", {"Action": "UpdatePullToPushAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"GetActivityAPI":
ApiInfo("GET", "/", {"Action": "GetActivityAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"GetStreamsAPI":
ApiInfo("GET", "/", {"Action": "GetStreamsAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"UpdateActivityBasicConfigAPI":
ApiInfo("POST", "/", {"Action": "UpdateActivityBasicConfigAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"GetActivityBasicConfigAPI":
ApiInfo("GET", "/", {"Action": "GetActivityBasicConfigAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"UpdateLoopVideoAPI":
ApiInfo("POST", "/", {"Action": "UpdateLoopVideoAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"UpdateLoopVideoStatusAPI":
ApiInfo("POST", "/", {"Action": "UpdateLoopVideoStatusAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
# 回放管理类接口
"UploadReplayAPI":
ApiInfo("POST", "/", {"Action": "UploadReplayAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"UpdateMediaOnlineStatusAPI":
ApiInfo("POST", "/", {"Action": "UpdateMediaOnlineStatusAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"ListMediasAPI":
ApiInfo("GET", "/", {"Action": "ListMediasAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
# 评论类接口
"PresenterChatAPI":
ApiInfo("POST", "/", {"Action": "PresenterChatAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
"DeleteChatAPI":
ApiInfo("POST", "/", {"Action": "DeleteChatAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
# 客户端SDK类接口
"GetSDKTokenAPI":
ApiInfo("POST", "/", {"Action": "GetSDKTokenAPI", "Version": LIVESAAS_API_VERSION}, {}, {}),
}
class LivesaasService(Service):
def __init__(self, region=REGION_CN_NORTH1):
self.service_info = LivesaasService.get_service_info(region)
self.api_info = LivesaasService.get_api_info()
super(LivesaasService, self).__init__(self.service_info, self.api_info)
@staticmethod
def get_service_info(region):
service_info = service_info_map.get(region, None)
if not service_info:
raise Exception('LivesaasService not support region %s' % region)
return service_info
@staticmethod
def get_api_info():
return api_info
async def async_json(self, api, params, body):
if not (api in self.api_info):
raise Exception("no such api")
api_info = self.api_info[api]
r = self.prepare_request(api_info, params)
r.headers['Content-Type'] = 'application/json'
r.body = body
timeout = aiohttp.ClientTimeout(connect=self.service_info.connection_timeout, sock_connect=self.service_info.socket_timeout)
SignerV4.sign(r, self.service_info.credentials)
url = r.build()
async with aiohttp.request("POST", url, headers=r.headers, data=r.body, timeout=timeout) as r:
resp = await r.text(encoding="utf-8")
if r.status == 200:
return resp
else:
raise Exception(resp)
async def async_get(self, api, params):
if not (api in self.api_info):
raise Exception("no such api")
api_info = self.api_info[api]
r = self.prepare_request(api_info, params)
timeout = aiohttp.ClientTimeout(connect=self.service_info.connection_timeout, sock_connect=self.service_info.socket_timeout)
SignerV4.sign(r, self.service_info.credentials)
url = r.build()
async with aiohttp.request("GET", url, headers=r.headers, timeout=timeout) as r:
resp = await r.text(encoding="utf-8")
if r.status == 200:
return resp
else:
raise Exception(resp)
# ========================= 直播间管理类接口 =========================
# CreateActivityAPI
def create_activity_api(self, body):
res = self.json('CreateActivityAPI', dict(), json.dumps(body))
if res == '':
raise Exception("CreateActivityAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_create_activity_api(self, body):
res = await self.async_json('CreateActivityAPI', dict(), json.dumps(body))
if res == '':
raise Exception("CreateActivityAPI: empty response")
res_json = json.loads(res)
return res_json
# DeleteActivityAPI
def delete_activity_api(self, body):
res = self.json('DeleteActivityAPI', dict(), json.dumps(body))
if res == '':
raise Exception("DeleteActivityAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_delete_activity_api(self, body):
res = await self.async_json('DeleteActivityAPI', dict(), json.dumps(body))
if res == '':
raise Exception("DeleteActivityAPI: empty response")
res_json = json.loads(res)
return res_json
# ListActivityAPI
def list_activity_api(self, params):
res = self.get('ListActivityAPI', params)
if res == '':
raise Exception("ListActivityAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_list_activity_api(self, params):
res = await self.async_get('ListActivityAPI', params)
if res == '':
raise Exception("ListActivityAPI: empty response")
res_json = json.loads(res)
return res_json
# ========================= 直播间控制类接口 =========================
# UpdateActivityStatusAPI
def update_activity_status_api(self, body):
res = self.json('UpdateActivityStatusAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateActivityStatusAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_update_activity_status_api(self, body):
res = await self.async_json('UpdateActivityStatusAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateActivityStatusAPI: empty response")
res_json = json.loads(res)
return res_json
# UpdatePullToPushAPI
def update_pull_to_push_api(self, body):
res = self.json('UpdatePullToPushAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdatePullToPushAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_update_pull_to_push_api(self, body):
res = await self.async_json('UpdatePullToPushAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdatePullToPushAPI: empty response")
res_json = json.loads(res)
return res_json
# GetActivityAPI
def get_activity_api(self, params):
res = self.get('GetActivityAPI', params)
if res == '':
raise Exception("GetActivityAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_get_activity_api(self, params):
res = await self.async_get('GetActivityAPI', params)
if res == '':
raise Exception("GetActivityAPI: empty response")
res_json = json.loads(res)
return res_json
# GetStreamsAPI
def get_streams_api(self, params):
res = self.get('GetStreamsAPI', params)
if res == '':
raise Exception("GetStreamsAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_get_streams_api(self, params):
res = await self.async_get('GetStreamsAPI', params)
if res == '':
raise Exception("GetStreamsAPI: empty response")
res_json = json.loads(res)
return res_json
# UpdateActivityBasicConfigAPI
def update_activity_basic_config_api(self, body):
res = self.json('UpdateActivityBasicConfigAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateActivityBasicConfigAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_update_activity_basic_config_api(self, body):
res = await self.async_json('UpdateActivityBasicConfigAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateActivityBasicConfigAPI: empty response")
res_json = json.loads(res)
return res_json
# GetActivityBasicConfigAPI
def get_activity_basic_config_api(self, params):
res = self.get('GetActivityBasicConfigAPI', params)
if res == '':
raise Exception("GetActivityBasicConfigAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_get_activity_basic_config_api(self, params):
res = await self.async_get('GetActivityBasicConfigAPI', params)
if res == '':
raise Exception("GetActivityBasicConfigAPI: empty response")
res_json = json.loads(res)
return res_json
# UpdateLoopVideoAPI
def update_loop_video_api(self, body):
res = self.json('UpdateLoopVideoAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateLoopVideoAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_update_loop_video_api(self, body):
res = await self.async_json('UpdateLoopVideoAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateLoopVideoAPI: empty response")
res_json = json.loads(res)
return res_json
# UpdateLoopVideoStatusAPI
def update_loop_video_status_api(self, body):
res = self.json('UpdateLoopVideoStatusAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateLoopVideoStatusAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_update_loop_video_status_api(self, body):
res = await self.async_json('UpdateLoopVideoStatusAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateLoopVideoStatusAPI: empty response")
res_json = json.loads(res)
return res_json
# ========================= 回放管理类接口 =========================
# UploadReplayAPI
def upload_replay_api(self, body):
res = self.json('UploadReplayAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UploadReplayAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_upload_replay_api(self, body):
res = await self.async_json('UploadReplayAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UploadReplayAPI: empty response")
res_json = json.loads(res)
return res_json
# UpdateMediaOnlineStatusAPI
def update_media_online_status_api(self, body):
res = self.json('UpdateMediaOnlineStatusAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateMediaOnlineStatusAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_update_media_online_status_api(self, body):
res = await self.async_json('UpdateMediaOnlineStatusAPI', dict(), json.dumps(body))
if res == '':
raise Exception("UpdateMediaOnlineStatusAPI: empty response")
res_json = json.loads(res)
return res_json
# ListMediasAPI
def list_medias_api(self, params):
res = self.get('ListMediasAPI', params)
if res == '':
raise Exception("ListMediasAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_list_medias_api(self, params):
res = await self.async_get('ListMediasAPI', params)
if res == '':
raise Exception("ListMediasAPI: empty response")
res_json = json.loads(res)
return res_json
# ========================= 评论类接口 =========================
# PresenterChatAPI
def presenter_chat_api(self, body):
res = self.json('PresenterChatAPI', dict(), json.dumps(body))
if res == '':
raise Exception("PresenterChatAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_presenter_chat_api(self, body):
res = await self.async_json('PresenterChatAPI', dict(), json.dumps(body))
if res == '':
raise Exception("PresenterChatAPI: empty response")
res_json = json.loads(res)
return res_json
# DeleteChatAPI
def delete_chat_api(self, body):
res = self.json('DeleteChatAPI', dict(), json.dumps(body))
if res == '':
raise Exception("DeleteChatAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_delete_chat_api(self, body):
res = await self.async_json('DeleteChatAPI', dict(), json.dumps(body))
if res == '':
raise Exception("DeleteChatAPI: empty response")
res_json = json.loads(res)
return res_json
# ========================= 客户端SDK类接口 =========================
# GetSDKTokenAPI
def get_sdk_token_api(self, body):
res = self.json('GetSDKTokenAPI', dict(), json.dumps(body))
if res == '':
raise Exception("GetSDKTokenAPI: empty response")
res_json = json.loads(res)
return res_json
async def async_get_sdk_token_api(self, body):
res = await self.async_json('GetSDKTokenAPI', dict(), json.dumps(body))
if res == '':
raise Exception("GetSDKTokenAPI: empty response")
res_json = json.loads(res)
return res_json