success('请求成功'); } // 向量引擎配置 private $baseUrl = "https://api.vectorengine.ai/v1"; private $apiKey = "sk-P877pnXMk2erRS2an7qEa3Kdb3rIb7JVAWZ39lhA8HeN71gZ"; // 从控制台获取 private $timeout = 120; // 超时时间(秒),视频生成需要更长 /** * 文生图接口 * POST /api/index/textToImage * 参数: prompt (string) 提示词 */ public function textToImage() { $params = $this->request->param(); if (empty($params)) { $this->error('提示词不能为空'); } $data = [ "model" => "gemini-3-pro-image-preview", // 可替换为 dall-e-3 等 "prompt" => $params['prompt'], "n" => 1, "size" => "1024x1024" ]; $result = $this->requestVectorEngine("/images/generations", $data); if ($result['code'] === 0) { $this->success('生成成功', $result['data']); } else { $this->error($result['msg'], $result['data']); } } /** * 图生文接口 * POST /api/index/imageToText * 参数: image_url (string) 公网图片URL, prompt (string) 提问指令 */ public function imageToText() { $imageUrl = $this->request->post('image_url'); $prompt = $this->request->post('prompt', '描述这张图片的内容'); if (empty($imageUrl)) { $this->error('图片URL不能为空'); } $data = [ "model" => "gpt-4-vision-preview", "messages" => [ [ "role" => "user", "content" => [ ["type" => "text", "text" => $prompt], ["type" => "image_url", "image_url" => ["url" => $imageUrl]] ] ] ], "max_tokens" => 1000 ]; $result = $this->requestVectorEngine("/chat/completions", $data); if ($result['code'] === 0) { $this->success('识别成功', $result['data']); } else { $this->error($result['msg'], $result['data']); } } /** * 文生视频接口 * POST /api/index/textToVideo * 参数: prompt (string) 提示词, duration (int) 时长(秒), resolution (string) 分辨率 */ public function textToVideo() { $prompt = $this->request->post('prompt'); $duration = $this->request->post('duration', 5); $resolution = $this->request->post('resolution', '720p'); if (empty($prompt)) { $this->error('提示词不能为空'); } $data = [ "model" => "kling-1.6", // 可替换为 seedance-2.0 等 "prompt" => $prompt, "duration" => (int)$duration, "resolution" => $resolution ]; $result = $this->requestVectorEngine("/videos/generations", $data); if ($result['code'] === 0) { $this->success('生成成功', $result['data']); } else { $this->error($result['msg'], $result['data']); } } /** * 封装向量引擎通用CURL请求 */ private function requestVectorEngine($endpoint, $data) { $url = $this->baseUrl . $endpoint; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "Authorization: Bearer " . $this->apiKey ], CURLOPT_SSL_VERIFYPEER => false, // 生产环境建议开启 CURLOPT_TIMEOUT => $this->timeout ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error) { return ['code' => -1, 'msg' => '请求失败: ' . $error, 'data' => null]; } if ($httpCode !== 200) { return ['code' => $httpCode, 'msg' => 'API返回异常', 'data' => json_decode($response, true)]; } return ['code' => 0, 'msg' => '成功', 'data' => json_decode($response, true)]; } public function GetTxtToImg(){ $params = $this->request->param(); $prompt = $params['prompt'];//提示词 $model = $params['model'];//模型 $size = $params['size'];//尺寸 // 调用AI生成图片 $aiGateway = new AIGatewayService(); $res = $aiGateway->callDalleApi($prompt, $model, $size); // 提取base64图片数据 $imageData = ''; $imageType = 'png'; // 默认图片类型 if (isset($res['candidates'][0]['content']['parts'][0]['text'])) { $text_content = $res['candidates'][0]['content']['parts'][0]['text']; // 匹配base64图片数据和类型 if (preg_match('/data:image\/([a-zA-Z0-9]+);base64,([^\s]+)/', $text_content, $matches)) { $imageType = strtolower($matches[1]); $base64Data = $matches[2]; // 解码base64数据 $imageData = base64_decode($base64Data); } } if (!$imageData) { return json(['code' => 1, 'msg' => '图片生成失败,未找到有效图片数据']); } // 创建保存目录(public/uploads/log/YYYY-MM/) $yearMonth = date('Ym'); $saveDir = ROOT_PATH . 'public/uploads/ceshi/' . $yearMonth . '/'; if (!is_dir($saveDir)) { mkdir($saveDir, 0755, true); } // 生成唯一文件名 $fileName = uniqid() . '.' . $imageType; $filePath = $saveDir . $fileName; // 保存图片到本地文件 if (file_put_contents($filePath, $imageData) === false) { return json(['code' => 1, 'msg' => '图片保存失败']); } // 生成前端可访问的URL $imageUrl = '/uploads/ceshi/' . $yearMonth . '/' . $fileName; // 返回标准JSON响应 return json([ 'code' => 0, 'msg' => '图片生成成功', 'data' => [ 'url' => $imageUrl ] ]); } /** * 学生端文生视频接口 - 用于生成视频 */ public function GetTxtToVideo(){ $aiGateway = new AIGatewayService(); $apiUrl = $aiGateway->config['videos']['api_url']; $apiKey = $aiGateway->config['videos']['api_key']; // 获取并验证参数 $params = $this->request->param(); // echo "
";
// print_r($params);
// echo "";die;
$postData = [
'prompt' => $params['prompt'],
'model' => $params['model'],
'seconds' => $params['duration'],
'size' => $params['size'],
];
// 初始化CURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey
]);
curl_setopt($ch, CURLOPT_TIMEOUT, 300);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_HEADER, true); // 获取响应头
curl_setopt($ch, CURLOPT_VERBOSE, true); // 启用详细输出以进行调试
// 创建临时文件来捕获详细的cURL输出
$verbose = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
// 执行请求
$response = curl_exec($ch);
//HTTP状态码
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// 获取详细的cURL调试信息
rewind($verbose);
//CURL调试信息
$verboseLog = stream_get_contents($verbose);
fclose($verbose);
// 分离头部和主体
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
//响应头部
$header = substr($response, 0, $header_size);
//响应主体
$body = substr($response, $header_size);
// 检查CURL错误
$curlError = curl_error($ch);
curl_close($ch);
$responseData = json_decode($body, true);
// 检查API是否返回了错误信息
if (isset($responseData['error'])) {
$errorMessage = isset($responseData['error']['message']) ? $responseData['error']['message'] : 'API请求失败';
return json([
'code' => 1,
'msg' => '视频生成请求失败',
'data' => [
'error_type' => isset($responseData['error']['type']) ? $responseData['error']['type'] : 'unknown',
'error_code' => isset($responseData['error']['code']) ? $responseData['error']['code'] : 'unknown',
'error_message' => $errorMessage
]
]);
}
// 检查是否有自定义错误格式
if (isset($responseData['code']) && $responseData['code'] === 'fail_to_fetch_task' && isset($responseData['message'])) {
return json([
'code' => 1,
'msg' => '视频生成请求失败',
'data' => [
'error_code' => $responseData['code'],
'error_message' => $responseData['message']
]
]);
}
// 检查是否存在id字段
if (!isset($responseData['id'])) {
return json([
'code' => 1,
'msg' => '无法获取视频ID',
'data' => [
'response_data' => $responseData,
'http_code' => $httpCode
]
]);
}
// 1. 先检查视频状态
$statusUrl = 'https://chatapi.onechats.ai/v1/videos/' . $responseData['id'];
$statusData = $this->fetchVideoStatus($statusUrl, $apiKey);
// 检查视频状态
if ($statusData['status'] !== 'completed') {
return json([
'code' => 202,
'msg' => '视频尚未生成完成',
'data' => [
'video_id' => $responseData['id'],
'status' => $statusData['status'],
'progress' => $statusData['progress'],
'created_at' => $statusData['created_at'],
'message' => '请稍后再试,视频仍在' . ($statusData['status'] === 'queued' ? '排队中' : '处理中')
]
]);
}
// 2. 视频生成完成,准备下载
$apiUrl = 'https://chatapi.onechats.ai/v1/videos/' . $responseData['id'] . '/content';
// 获取可选的variant参数
$variant = $this->request->get('variant', '');
if (!empty($variant)) {
$apiUrl .= '?variant=' . urlencode($variant);
}
// 创建保存目录
$saveDir = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'videos' . DS . date('Ymd');
if (!is_dir($saveDir)) {
mkdir($saveDir, 0755, true);
}
// 生成唯一文件名
$filename = $responseData['id'] . '.mp4';
$localPath = DS . 'uploads' . DS . 'videos' . DS . date('Ymd') . DS . $filename;
$fullPath = $saveDir . DS . $filename;
// 3. 下载视频
$videoData = $this->downloadVideo($apiUrl, $apiKey);
// 4. 保存视频文件
if (file_put_contents($fullPath, $videoData) === false) {
throw new Exception('视频保存失败');
}
// 确保路径使用正斜杠,并只保存相对路径部分
$localPath = str_replace('\\', '/', $localPath);
// 移除开头的斜杠,确保路径格式为uploads/videos/...
$savePath = ltrim($localPath, '/');
// 返回成功响应
return json([
'code' => 0,
'msg' => '视频下载成功',
'data' => [
'video_id' => $responseData['id'],
'web_url' => $savePath
]
]);
}
/**
* 学生端视频状态查询接口 - 用于通过video_id查询视频状态
*/
public function Getvideo_id(){
// 获取并验证参数
$params = $this->request->param();
$videoId = $params['video_id'] ?? '';
// 验证video_id参数
if (empty($videoId)) {
return json([
'code' => 1,
'msg' => '缺少必要参数:video_id',
'data' => []
]);
}
$aiGateway = new AIGatewayService();
// $apiUrl = $aiGateway->config['videos']['api_url'];
$apiKey = $aiGateway->config['videos']['api_key'];
$apiUrl = 'https://chatapi.onechats.ai/v1/videos/' . $videoId;
// 先检查本地是否已经有该视频
$localVideoPath = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'videos' . DS . date('Ymd') . DS . $videoId . '.mp4';
if (file_exists($localVideoPath)) {
// 视频已存在本地,直接返回
$webPath = '/uploads/videos/' . date('Ymd') . '/' . $videoId . '.mp4';
return json([
'code' => 0,
'msg' => '视频下载成功',
'data' => [
'video_id' => $videoId,
'web_url' => substr($webPath, 1) // 移除开头的斜杠
]
]);
}
// 检查视频状态
$statusData = $this->fetchVideoStatus($apiUrl, $apiKey);
// 检查视频状态
if ($statusData['status'] !== 'completed') {
return json([
'code' => 202,
'msg' => '视频尚未生成完成',
'data' => [
'video_id' => $videoId,
'status' => $statusData['status'],
'progress' => isset($statusData['progress']) ? $statusData['progress'] : 0,
'created_at' => $statusData['created_at'],
'message' => '请稍后再试,视频仍在' . ($statusData['status'] === 'queued' ? '排队中' : '处理中')
]
]);
}
// 视频生成完成,下载视频
$downloadUrl = 'https://chatapi.onechats.ai/v1/videos/' . $videoId . '/content';
// 获取可选的variant参数
$variant = $this->request->get('variant', '');
if (!empty($variant)) {
$downloadUrl .= '?variant=' . urlencode($variant);
}
// 创建保存目录
$saveDir = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'videos' . DS . date('Ymd');
if (!is_dir($saveDir)) {
mkdir($saveDir, 0755, true);
}
// 生成唯一文件名
$filename = $videoId . '.mp4';
$localPath = DS . 'uploads' . DS . 'videos' . DS . date('Ymd') . DS . $filename;
$fullPath = $saveDir . DS . $filename;
// 下载视频
$videoData = $this->downloadVideo($downloadUrl, $apiKey);
// 保存视频文件
if (file_put_contents($fullPath, $videoData) === false) {
throw new Exception('视频保存失败');
}
// 确保路径使用正斜杠,并只保存相对路径部分
$localPath = str_replace(DIRECTORY_SEPARATOR, '/', $localPath);
// 移除开头的斜杠,确保路径格式为uploads/videos/...
$savePath = ltrim($localPath, '/');
// 返回成功响应
return json([
'code' => 0,
'msg' => '视频下载成功',
'data' => [
'video_id' => $videoId,
'web_url' => $savePath
]
]);
}
/**
* 九个分镜头生成流程
* 模型:gemini-3-pro-image-preview
* 说明:
* 第一步:(提示词 + 原始图片 = 九个分镜头图片) 或 (提示词 = 九个分镜头图片)
* 第二步:使用九个分镜头图进行裁剪单图生成连贯视频
* 第三步:在通过分镜头视频拼接成一个完整的视频(列如每个分镜头视频为8秒,九个为72秒形成完整视频)
*/
public function Get_txttonineimg()
{
// 发起接口请求
// $apiUrl = 'https://chatapi.onechats.ai/v1beta/models/gemini-3-pro-image-preview:generateContent';
$apiUrl = 'https://chatapi.onechats.ai/v1beta/models/gemini-3-pro-image-preview:streamGenerateContent';
$apiKey = '';
// $params = $this->request->param();
$prompt = '生成一个苹果(九个分镜头图片)';
$requestData = [
"contents" => [
[
"role" => "user",
"parts" => [
["text" => $prompt]
]
]
],
"generationConfig" => [
"responseModalities" => ["TEXT", "IMAGE"],
"imageConfig" => [
"aspectRatio" => "1:1"
]
]
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($requestData, JSON_UNESCAPED_UNICODE));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 开发环境临时关闭SSL验证
curl_setopt($ch, CURLOPT_TIMEOUT, 60); // 生成图片超时时间(建议60秒)
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$res = json_decode($response, true);
// 构建URL路径(使用正斜杠)
$url_path = '/uploads/txtnewimg/';
// 构建物理路径(使用正斜杠确保统一格式)
$save_path = ROOT_PATH . 'public' . '/' . 'uploads' . '/' . 'txtnewimg' . '/';
// 移除ROOT_PATH中可能存在的反斜杠,确保统一使用正斜杠
$save_path = str_replace('\\', '/', $save_path);
// 自动创建文件夹(如果不存在)
if (!is_dir($save_path)) {
mkdir($save_path, 0755, true);
}
// 提取base64图片数据
$text_content = $res['candidates'][0]['content']['parts'][0]['inlineData']['data'];
$str = 'data:image/jpeg;base64,';
$text_content = $str. $text_content;
// 匹配base64图片数据
preg_match('/data:image\/(png|jpg|jpeg);base64,([^"]+)/', $text_content, $matches);
if (empty($matches)) {
return '未找到图片数据';
}
$image_type = $matches[1];
$base64_data = $matches[2];
// 解码base64数据
$image_data = base64_decode($base64_data);
if ($image_data === false) {
return '图片解码失败';
}
// 生成唯一文件名(包含扩展名)
$file_name = uniqid() . '.' . $image_type;
$full_file_path = $save_path . $file_name;
// 保存图片到文件系统
if (!file_put_contents($full_file_path, $image_data)) {
return '图片保存失败';
}
// 生成数据库存储路径(使用正斜杠格式)
$db_img_path = $url_path . $file_name;
return $db_img_path;
}
}