2551 lines
93 KiB
PHP
2551 lines
93 KiB
PHP
<?php
|
||
|
||
namespace app\api\logic;
|
||
|
||
use app\api\logic\service\TokenLogService;
|
||
use app\common\enum\user\AccountLogEnum;
|
||
use app\common\logic\AccountLogLogic;
|
||
use app\common\model\human\HumanAnchor;
|
||
use app\common\model\human\HumanAudio;
|
||
use app\common\model\human\HumanTask;
|
||
use app\common\model\human\HumanVideo;
|
||
use app\common\model\human\HumanVideoTask;
|
||
use app\common\model\human\HumanVoice;
|
||
use app\common\model\user\User;
|
||
use app\common\service\FileService;
|
||
use think\db\exception\DataNotFoundException;
|
||
use think\db\exception\DbException;
|
||
use think\db\exception\ModelNotFoundException;
|
||
use app\common\model\user\UserTokensLog;
|
||
use think\facade\Log;
|
||
|
||
class HumanLogic extends ApiLogic
|
||
{
|
||
|
||
const AVATAR_TRAINING = 'avatarTraining'; //形象训练
|
||
const VOICE_TRAINING = 'voiceTraining'; //音色训练
|
||
const AUDIO_TRAINING = 'audioTraining'; //文字转音频
|
||
const VIDEO_TRAINING = 'videoTraining'; //视频训练
|
||
const COPYWRITING_CREATE = 'copywritingCreate'; //文案创作
|
||
const AVATAR_TRAINING_PRO = 'avatarTrainingPro'; //形象训练
|
||
const VOICE_TRAINING_PRO = 'voiceTrainingPro'; //音色训练
|
||
const AUDIO_TRAINING_PRO = 'audioTrainingPro'; //文字转音频
|
||
const VIDEO_TRAINING_PRO = 'videoTrainingPro'; //视频训练
|
||
|
||
const AVATAR_TRAINING_YM = 'avatarTrainingYm'; //形象训练
|
||
const VOICE_TRAINING_YM = 'voiceTrainingYm'; //音色训练
|
||
const AUDIO_TRAINING_YM = 'audioTrainingYm'; //文字转音频
|
||
const VIDEO_TRAINING_YM = 'videoTrainingYm'; //视频训练
|
||
|
||
|
||
const AVATAR_TRAINING_YMT = 'avatarTrainingYmt'; //形象训练
|
||
const VOICE_TRAINING_YMT = 'voiceTrainingYmt'; //音色训练
|
||
const AUDIO_TRAINING_YMT = 'audioTrainingYmt'; //文字转音频
|
||
const VIDEO_TRAINING_YMT = 'videoTrainingYmt'; //视频训练
|
||
|
||
/**
|
||
* 更新形象
|
||
* @param array $data
|
||
* @param string $modelVersion
|
||
* @return bool
|
||
* @throws DataNotFoundException
|
||
* @throws DbException
|
||
* @throws ModelNotFoundException
|
||
*/
|
||
public static function updateAnchor(array $data, string $modelVersion): bool
|
||
{
|
||
$model = HumanAnchor::where('model_version', $modelVersion)->where('status', 0);
|
||
|
||
if ($modelVersion == 1) {
|
||
|
||
$model = $model->where('anchor_id', $data['id']);
|
||
} elseif ($modelVersion == 2) {
|
||
// 目前是同步的 没有回调
|
||
|
||
return true;
|
||
} else {
|
||
|
||
return false;
|
||
}
|
||
|
||
$model->select()
|
||
->each(function ($item) use ($data) {
|
||
if ($item->model_version === 1) { //标准版
|
||
|
||
if (in_array($data['current_status'], ['completed', 'failed'])) {
|
||
$item->status = ($data['current_status'] == 'completed') ? 1 : 2;
|
||
|
||
// TODO 失败退费
|
||
if ($item->status == 2) {
|
||
self::refundTokens($item->user_id, $item->anchor_id, $item->task_id, 'human_anchor');
|
||
}
|
||
} else {
|
||
$item->status = 0;
|
||
}
|
||
} elseif ($item->model_version === 2) {
|
||
}
|
||
$item->save();
|
||
});
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 更新音色
|
||
* @param array $data
|
||
* @param string $modelVersion
|
||
* @return bool
|
||
* @throws DataNotFoundException
|
||
* @throws DbException
|
||
* @throws ModelNotFoundException
|
||
*/
|
||
public static function updateVoice(array $data, string $modelVersion): bool
|
||
{
|
||
|
||
//查询形象
|
||
$model = HumanVoice::where('model_version', $modelVersion)->where('status', 0);
|
||
if ($modelVersion == 1) {
|
||
$model = $model->where('voice_id', $data['id']);
|
||
} elseif ($modelVersion == 2) {
|
||
// 目前是同步的 没有回调
|
||
return true;
|
||
} else {
|
||
|
||
return false;
|
||
}
|
||
|
||
$model->select()
|
||
->each(function ($item) use ($data) {
|
||
if ($item->model_version === 1) { //标准版
|
||
if (in_array($data['current_status'], ['completed', 'failed'])) {
|
||
$item->status = ($data['current_status'] == 'completed') ? 1 : 2;
|
||
|
||
// TODO 失败退费
|
||
if ($item->status == 2) {
|
||
self::refundTokens($item->user_id, $item->anchor_id, $item->task_id, 'human_anchor');
|
||
}
|
||
|
||
} else {
|
||
$item->status = 0;
|
||
}
|
||
}
|
||
$item->save();
|
||
});
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 更新音频
|
||
* @param array $data
|
||
* @param string $modelVersion
|
||
* @return bool
|
||
* @throws DataNotFoundException
|
||
* @throws DbException
|
||
* @throws ModelNotFoundException
|
||
*/
|
||
public static function updateAudio(array $data, string $modelVersion): bool
|
||
{
|
||
|
||
//查询形象
|
||
$model = HumanAudio::where('model_version', $modelVersion)->where('status', 0);
|
||
if ($modelVersion == 1) {
|
||
$model = $model->where('audio_id', $data['id']);
|
||
} elseif ($modelVersion == 2) {
|
||
// 目前是同步的 没有回调
|
||
return true;
|
||
} else {
|
||
|
||
return false;
|
||
}
|
||
|
||
$model->select()
|
||
->each(function ($item) use ($data) {
|
||
|
||
if ($item->model_version === 1) { //标准版
|
||
|
||
$item->status = ($data['url'] != "") ? 1 : 2;
|
||
$item->url = FileService::downloadFileBySource($data['url'], 'audio');
|
||
|
||
// TODO 失败退费
|
||
if ($item->status == 2) {
|
||
self::refundTokens($item->user_id, $item->audio_id, $item->task_id, 'human_audio');
|
||
}
|
||
|
||
// 更新视频
|
||
HumanVideoTask::where('task_id', $item->task_id)->update([
|
||
'audio_url' => FileService::setFileUrl($item->url)
|
||
]);
|
||
}
|
||
$item->save();
|
||
});
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 更新视频
|
||
* @param array $data
|
||
* @param string $modelVersion
|
||
* @return bool
|
||
* @throws DataNotFoundException
|
||
* @throws DbException
|
||
* @throws ModelNotFoundException
|
||
*/
|
||
public static function updateVideo(array $data, string $modelVersion): bool
|
||
{
|
||
|
||
//查询形象
|
||
$model = HumanVideoTask::where('model_version', $modelVersion)->where('status', 0);
|
||
if ($modelVersion == 1) {
|
||
$model = $model->where('result_id', $data['id']);
|
||
} elseif (in_array($modelVersion,[4,6])) {
|
||
$model = $model->where('result_id', $data['job_id']);
|
||
}elseif ($modelVersion == 2) {
|
||
return true;
|
||
} else {
|
||
|
||
return false;
|
||
}
|
||
$model->select()
|
||
->each(function ($item) use ($data) {
|
||
if ($item->model_version === 1) { //标准版
|
||
|
||
if (in_array($data['current_status'], ['success', 'fail'])) {
|
||
$item->status = ($data['current_status'] == 'success') ? 1 : 2;
|
||
|
||
if ($item->status == 2) {
|
||
self::refundTokens($item->user_id, $item->result_id, $item->task_id, 'human_video');
|
||
}
|
||
|
||
} else {
|
||
$item->status = 0;
|
||
}
|
||
$item->result_url = FileService::downloadFileBySource($data['result'], 'video');
|
||
$item->remark = $data['msg'] ?? '';
|
||
}
|
||
if (in_array($item->model_version,[4,6])) { //高级版
|
||
//这里对应 status=3 或 status=4) 3成功 4失败
|
||
if (in_array($data['status'], [3,4])) {
|
||
$item->status = ($data['status'] == 3) ? 1 : 2;
|
||
$scene = $item->model_version == 4 ? "human_video_ym" : "human_video_ymt";
|
||
|
||
if($item->status == 2){
|
||
self::refundTokens($item->user_id, $item->result_id, $item->task_id, $scene);
|
||
}
|
||
|
||
} else {
|
||
$item->status = 0;
|
||
}
|
||
$item->result_url = FileService::downloadFileBySource($data['video_Url'], 'video');
|
||
$item->remark = $data['msg'] ?? '';
|
||
}
|
||
$item->save();
|
||
});
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @desc 退费
|
||
* @param int $userId
|
||
* @param int $id
|
||
* @param string $taskId
|
||
* @param string $type
|
||
* @return bool
|
||
*/
|
||
public static function refundTokens(int $userId, string $id, string $taskId, string $type): bool
|
||
{
|
||
|
||
try {
|
||
|
||
[$typeIndex, $typeID] = match ($type) {
|
||
'human_anchor' => [1, AccountLogEnum::TOKENS_DEC_HUMAN_AVATAR],
|
||
'human_voice' => [2, AccountLogEnum::TOKENS_DEC_HUMAN_VOICE],
|
||
'human_audio' => [3, AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO],
|
||
'human_video' => [4, AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO],
|
||
'human_anchor_pro' => [1, AccountLogEnum::TOKENS_DEC_HUMAN_AVATAR_PRO],
|
||
'human_voice_pro' => [2, AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_PRO],
|
||
'human_audio_pro' => [3, AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_PRO],
|
||
'human_video_pro' => [4, AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_PRO],
|
||
'human_anchor_ym' => [1, AccountLogEnum::TOKENS_DEC_HUMAN_AVATAR_YM],
|
||
'human_voice_ym' => [2, AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_YM],
|
||
'human_audio_ym' => [3, AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YM],
|
||
'human_video_ym' => [4, AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_YM],
|
||
'human_anchor_ymt' => [1, AccountLogEnum::TOKENS_DEC_HUMAN_AVATAR_YMT],
|
||
'human_voice_ymt' => [2, AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_YMT],
|
||
'human_audio_ymt' => [3, AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YMT],
|
||
'human_video_ymt' => [4, AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_YMT],
|
||
};
|
||
// 请求查询接口
|
||
$requestParams = [
|
||
'id' => $id,
|
||
'task_id' => $taskId,
|
||
'type' => $typeIndex
|
||
];
|
||
|
||
if (strpos($type, '_ymt') !== false) {
|
||
$response = \app\common\service\ToolsService::Human()->detailYmt($requestParams);
|
||
}elseif (strpos($type, '_ym') !== false) {
|
||
$response = \app\common\service\ToolsService::Human()->detailYm($requestParams);
|
||
}elseif (strpos($type, '_pro') !== false) {
|
||
$response = \app\common\service\ToolsService::Human()->detailPro($requestParams);
|
||
} else {
|
||
$response = \app\common\service\ToolsService::Human()->detail($requestParams);
|
||
}
|
||
|
||
if(isset($response['data']['task_status']) && $response['data']['task_status'] == 1) {
|
||
return true;
|
||
}
|
||
|
||
//查询是否已返还
|
||
if (UserTokensLog::where('user_id', $userId)->where('change_type', $typeID)->where('action', 1)->where('task_id', $taskId)->count() == 0) {
|
||
|
||
$points = UserTokensLog::where('user_id', $userId)->where('change_type', $typeID)->where('task_id', $taskId)->value('change_amount') ?? 0;
|
||
|
||
AccountLogLogic::recordUserTokensLog(false, $userId, $typeID, $points, $taskId);
|
||
}
|
||
|
||
return true;
|
||
} catch (\Throwable $e) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @desc 视频生成任务
|
||
* @param $request
|
||
* @return bool
|
||
* @date 2024/9/30 17:55
|
||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||
* @author dagouzi
|
||
*/
|
||
public static function videoTask($request)
|
||
{
|
||
try {
|
||
$anchor_id = $request['anchor_id'] ?? '';
|
||
$anchor_name = $request['anchor_name'] ?? '';
|
||
$name = $request['name'] ?? '';
|
||
$gender = $request['gender'] ?? 'male';
|
||
$pic = $request['pic'] ?? '';
|
||
$video_url = $request['video_url'] ?? '';
|
||
$msg = $request['msg'] ?? '';
|
||
$audio_url = $request['audio_url'] ?? '';
|
||
$model_version = $request['model_version'] ?? '';
|
||
$voice_id = $request['voice_id'] ?? '';
|
||
$voice_name = $request['voice_name'] ?? '';
|
||
$voice_type = $request['voice_type'] ?? 1;
|
||
|
||
if (empty($anchor_name) || empty($name) || !in_array($model_version, [1, 2, 4, 6]) || !in_array($gender, ['male', 'female'])) {
|
||
|
||
throw new \Exception('参数错误');
|
||
}
|
||
|
||
|
||
|
||
//生成一个唯一任务ID
|
||
$taskId = generate_unique_task_id();
|
||
|
||
// 驱动类型 1:文字驱动 2:音频驱动
|
||
$audio_type = $request['audio_type'] ?? 0;
|
||
|
||
switch ($audio_type) {
|
||
case 1: // 文字驱动
|
||
//模型1 字数超过150字节
|
||
if ($model_version == 1 && mb_strlen($msg) > 150) {
|
||
|
||
throw new \Exception('字数不能超过150字节');
|
||
}
|
||
|
||
//模型2 字数超过30000字节
|
||
if ($model_version == 2 && mb_strlen($msg) > 30000) {
|
||
|
||
throw new \Exception('字数不能超过30000字节');
|
||
}
|
||
//模型2 字数超过2000字
|
||
if (in_array($model_version, [4, 6]) && mb_strlen($msg, 'UTF-8') > 6200) {
|
||
|
||
throw new \Exception('字数不能超过2000');
|
||
}
|
||
|
||
break;
|
||
case 2: // 音频驱动
|
||
$msg = '';
|
||
if (in_array($model_version, [1])) {
|
||
|
||
throw new \Exception('目前不支持标准版');
|
||
}
|
||
|
||
// 音频驱动 音频链接不能为空
|
||
if (empty($audio_url)) {
|
||
throw new \Exception('音频文件不能为空');
|
||
}
|
||
break;
|
||
default:
|
||
throw new \Exception('参数错误');
|
||
}
|
||
|
||
//标题 只能有数字与字母、中文组成
|
||
$pattern = '/^[a-zA-Z0-9\x{4e00}-\x{9fa5}]*$/u';
|
||
|
||
if (!preg_match($pattern, $name)) {
|
||
|
||
throw new \Exception('标题只能有数字与字母、中文组成, 且10个字符以内');
|
||
}
|
||
// 验证余额
|
||
if ($model_version == 1) {
|
||
TokenLogService::checkToken(self::$uid, self::VIDEO_TRAINING);
|
||
} elseif ($model_version == 4) {
|
||
TokenLogService::checkToken(self::$uid, self::VIDEO_TRAINING_YM);
|
||
} elseif ($model_version == 6) {
|
||
TokenLogService::checkToken(self::$uid, self::VIDEO_TRAINING_YMT);
|
||
}else {
|
||
TokenLogService::checkToken(self::$uid, self::VIDEO_TRAINING_PRO);
|
||
}
|
||
|
||
//如果用户有任务在处理中,不允许创建任务
|
||
$taskCount = HumanVideoTask::where('user_id', self::$uid)
|
||
->where('status', 0)
|
||
->where('model_version', $model_version)
|
||
->count();
|
||
|
||
if ($taskCount >= 5) {
|
||
|
||
throw new \Exception('当前您已有5个任务正在排队处理中,请等待任务完成后再创建');
|
||
}
|
||
|
||
|
||
// 形象ID 已有形象
|
||
if ($anchor_id) {
|
||
$anchor = HumanAnchor::where('anchor_id', $anchor_id)->findOrEmpty();
|
||
if ($anchor->isEmpty()) {
|
||
throw new \Exception('形象不存在');
|
||
}
|
||
$video_url = $anchor['url'];
|
||
}
|
||
if ( $pic == ''){
|
||
$pic = self::getVideoThumbnailFromUrl($video_url);
|
||
if ( $pic == false){
|
||
Log::write('获取图片任务失败' . $pic);
|
||
throw new \Exception('封面获取失败');
|
||
}
|
||
}
|
||
|
||
// 音色ID 已有音色
|
||
if ($voice_id && $voice_type == 1) {
|
||
$voice = HumanVoice::where('voice_id', $voice_id)->findOrEmpty();
|
||
if ($voice->isEmpty()) {
|
||
throw new \Exception('音色不存在');
|
||
}
|
||
$voice_name = $voice['name'];
|
||
}elseif ($voice_id && $voice_type == 0){
|
||
$voice_id = HumanVoice::getBuiltInVoice($voice_id,$model_version);
|
||
if ($voice_name == '') {
|
||
throw new \Exception('音色名称不能为空');
|
||
}
|
||
if ($voice_id === '00000') {
|
||
throw new \Exception('音色错误');
|
||
}
|
||
}
|
||
|
||
$videoTaskData = [
|
||
'user_id' => self::$uid,
|
||
'name' => $name,
|
||
'pic' => $pic,
|
||
'gender' => $gender,
|
||
'audio_type' => $audio_type,
|
||
'anchor_id' => $anchor_id,
|
||
'anchor_name' => $anchor_name,
|
||
'voice_id' => $voice_id,
|
||
'voice_name' => $voice_name,
|
||
'msg' => $msg,
|
||
'task_id' => $taskId,
|
||
'model_version' => $model_version,
|
||
'upload_video_url' => $video_url,
|
||
'upload_audio_url' => $audio_url,
|
||
];
|
||
|
||
if ($audio_type == 2) {
|
||
$videoTaskData['audio_url'] = $audio_url;
|
||
}
|
||
$videoTask = HumanVideoTask::create($videoTaskData);
|
||
|
||
self::$returnData = $videoTask->toArray();
|
||
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
self::setError($e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @desc 创建形象
|
||
* @param $data
|
||
* @return bool
|
||
* @throws \Exception
|
||
* @date 2024/9/28 17:46
|
||
* @author dagouzi
|
||
*/
|
||
public static function createAnchor($data)
|
||
{
|
||
// 检查余额
|
||
if ($data['model_version'] == 1) {
|
||
TokenLogService::checkToken(self::$uid, 'human_anchor');
|
||
} elseif ($data['model_version'] == 4){
|
||
TokenLogService::checkToken(self::$uid, 'human_anchor_ym');
|
||
}elseif ($data['model_version'] == 6){
|
||
TokenLogService::checkToken(self::$uid, 'human_anchor_ymt');
|
||
}else {
|
||
TokenLogService::checkToken(self::$uid, 'human_anchor_pro');
|
||
}
|
||
|
||
$name = $data['name'] ?? '';
|
||
$gender = $data['gender'] ?? 'male';
|
||
$anchor_url = $data['url'] ?? '';
|
||
$model_version = $data['model_version'] ?? '';
|
||
if (empty($name) || !in_array($model_version, [1, 2, 4, 6]) || !in_array($gender, ['male', 'female']) || empty($anchor_url)) {
|
||
message('参数错误');
|
||
}
|
||
|
||
//标题 只能有数字与字母、中文组成
|
||
$pattern = '/^[a-zA-Z0-9\x{4e00}-\x{9fa5}]*$/u';
|
||
|
||
if (!preg_match($pattern, $name)) {
|
||
|
||
message('标题只能有数字与字母、中文组成, 且10个字符以内');
|
||
}
|
||
$pic = $data['pic'] ?? '';
|
||
if ($pic == ''){
|
||
$pic = self::getVideoThumbnailFromUrl($anchor_url);
|
||
if ( $pic == false){
|
||
self::setError('封面获取失败');
|
||
return false;
|
||
}
|
||
}
|
||
$taskId = generate_unique_task_id();
|
||
|
||
if (in_array($data['model_version'] ,[2, 4, 6])) {
|
||
|
||
$addData = [
|
||
'user_id' => self::$uid,
|
||
'status' => 1,
|
||
'anchor_id' => uniqid(),
|
||
'name' => $name,
|
||
'gender' => $gender,
|
||
'url' => $anchor_url,
|
||
'task_id' => $taskId,
|
||
'model_version' => $model_version,
|
||
'pic' => $pic,
|
||
];
|
||
$anchor = HumanAnchor::create($addData);
|
||
|
||
self::$returnData = [
|
||
'id' => $anchor->anchor_id,
|
||
'pic' => $pic,
|
||
'picurl' => FileService::getFileUrl($pic),
|
||
];
|
||
return true;
|
||
}
|
||
|
||
$request = [
|
||
'video_url' => $anchor_url,
|
||
'name' => $name,
|
||
'gender' => $gender,
|
||
];
|
||
switch ($model_version)
|
||
{
|
||
case 1:
|
||
$scene = self::AVATAR_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::AVATAR_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::AVATAR_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::AVATAR_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::AVATAR_TRAINING;
|
||
break;
|
||
}
|
||
|
||
$result = self::requestUrl($request, $scene, self::$uid, $taskId);
|
||
|
||
if (!empty($result) && isset($result['id'])) {
|
||
|
||
$result['pic'] = $pic;
|
||
$result['picurl'] = FileService::getFileUrl($pic);
|
||
self::$returnData = $result;
|
||
$addData = [
|
||
'user_id' => self::$uid,
|
||
'status' => 0,
|
||
'anchor_id' => $result['id'],
|
||
'name' => $name,
|
||
'gender' => $gender,
|
||
'url' => $anchor_url,
|
||
'task_id' => $taskId,
|
||
'model_version' => $model_version,
|
||
'pic' => $pic
|
||
];
|
||
HumanAnchor::create($addData);
|
||
} else {
|
||
self::setError('合成失败');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
/**
|
||
* @desc 形象重试
|
||
* @param $data
|
||
* @return bool
|
||
* @throws \Exception
|
||
* @date 2024/9/28 17:46
|
||
* @author dagouzi
|
||
*/
|
||
public static function anchorRetry(int $id)
|
||
{
|
||
$anchor = HumanAnchor::where('id', $id)->where('user_id', self::$uid)->findOrEmpty();
|
||
if ($anchor->isEmpty()) {
|
||
|
||
message('形象不存在');
|
||
}
|
||
|
||
if ($anchor->model_version == 2) {
|
||
message('当前模型无需重试');
|
||
}
|
||
|
||
//状态不对
|
||
if ($anchor->status != 2) {
|
||
message('当前任务不是失败状态,请勿提交');
|
||
}
|
||
|
||
//提交时间间隔1分钟
|
||
if (strtotime($anchor->update_time) > strtotime('-1 minute')) {
|
||
message('任务训练间隔1分钟,请勿频繁提交');
|
||
}
|
||
|
||
//保存更新时间
|
||
$anchor->update_time = time();
|
||
$anchor->save();
|
||
|
||
$request = [
|
||
'video_url' => $anchor->url,
|
||
'name' => $anchor->name,
|
||
'gender' => $anchor->gender,
|
||
];
|
||
|
||
switch ( $anchor->model_version)
|
||
{
|
||
case 1:
|
||
$scene = self::AVATAR_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::AVATAR_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::AVATAR_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::AVATAR_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::AVATAR_TRAINING;
|
||
break;
|
||
}
|
||
|
||
$result = self::requestUrl($request, $scene, $anchor->user_id, $anchor->task_id);
|
||
|
||
if (!empty($result) && isset($result['id'])) {
|
||
|
||
$anchor->anchor_id = $result['id'];
|
||
$anchor->status = 0;
|
||
$anchor->save();
|
||
self::$returnData = $anchor->toArray();
|
||
} else {
|
||
|
||
self::setError('重试失败');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @desc 形象列表
|
||
* @param $data
|
||
* @return array
|
||
* @throws DataNotFoundException
|
||
* @throws DbException
|
||
* @throws ModelNotFoundException
|
||
* @date 2024/9/28 18:25
|
||
* @author dagouzi
|
||
*/
|
||
public static function anchorLists($data): array
|
||
{
|
||
//TODO 新增分页
|
||
$pageNo = ($data['page_no'] - 1) * $data['page_size'];
|
||
$pageSize = $data['page_size'];
|
||
$modelVersion = $data['model_version'] ?? '';
|
||
$status = $data['status'] ?? '';
|
||
$name = $data['name'] ?? '';
|
||
$result = HumanAnchor::where(['user_id' => self::$uid])
|
||
->when($name, function ($query) use ($name) {
|
||
$query->where('name', 'like', '%' . $name . '%');
|
||
})
|
||
->when($modelVersion, function ($query) use ($modelVersion) {
|
||
$query->where('model_version', $modelVersion);
|
||
})
|
||
->when($status != "", function ($query) use ($status) {
|
||
$query->where('status', $status);
|
||
})
|
||
->limit($pageNo, $pageSize)
|
||
->order('create_time', 'desc')
|
||
->select()
|
||
->toArray();
|
||
|
||
$data = [
|
||
'lists' => $result,
|
||
'count' => HumanAnchor::where(['user_id' => self::$uid])
|
||
->when($modelVersion, function ($query) use ($modelVersion) {
|
||
$query->where('model_version', $modelVersion);
|
||
})
|
||
->count(),
|
||
'page_no' => $data['page_no'],
|
||
'page_size' => $data['page_size'],
|
||
];
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* @desc 删除形象
|
||
* @param array $data
|
||
* @return bool
|
||
* @date 2024/9/28 18:31
|
||
* @author dagouzi
|
||
*/
|
||
public static function anchorDelete(array $data)
|
||
{
|
||
|
||
try {
|
||
|
||
if (is_string($data['id'])) {
|
||
HumanAnchor::destroy(['id' => $data['id'], 'user_id' => self::$uid]);
|
||
} else {
|
||
HumanAnchor::whereIn('id', $data['id'])->where('user_id', self::$uid)->select()->delete();
|
||
}
|
||
|
||
return true;
|
||
} catch (\Exception $exception) {
|
||
self::setError($exception->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @desc 语音克隆
|
||
* @param $data
|
||
* @return bool
|
||
* @date 2024/9/28 18:36
|
||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||
* @author dagouzi
|
||
*/
|
||
public static function createVoice($data)
|
||
{
|
||
// 检查余额
|
||
if ($data['model_version'] == 1) {
|
||
TokenLogService::checkToken(self::$uid, 'human_voice');
|
||
}elseif ($data['model_version'] == 4) {
|
||
TokenLogService::checkToken(self::$uid, 'human_voice_ym');
|
||
}elseif ($data['model_version'] == 6) {
|
||
TokenLogService::checkToken(self::$uid, 'human_voice_ymt');
|
||
}else {
|
||
TokenLogService::checkToken(self::$uid, 'human_voice_pro');
|
||
}
|
||
$name = $data['name'] ?? '';
|
||
$gender = $data['gender'] ?? '';
|
||
$voice_url = $data['url'] ?? '';
|
||
$model_version = $data['model_version'] ?? '';
|
||
if (empty($name) || !in_array($model_version, [1, 2, 4, 6]) || !in_array($gender, ['male', 'female']) || empty($voice_url)) {
|
||
message('参数错误');
|
||
}
|
||
|
||
//标题
|
||
$pattern = '/^[a-zA-Z0-9\p{Han}]{1,10}$/u';
|
||
|
||
if (!preg_match($pattern, $name)) {
|
||
|
||
message('标题只能有数字与字母、中文组成, 且10个字符以内');
|
||
}
|
||
|
||
$taskId = generate_unique_task_id();
|
||
|
||
$request = [
|
||
'name' => $name,
|
||
'gender' => $gender,
|
||
'audio_url' => $voice_url
|
||
];
|
||
|
||
switch ($model_version) {
|
||
case 1:
|
||
$scene = self::VOICE_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::VOICE_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::VOICE_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::VOICE_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::VOICE_TRAINING_PRO;
|
||
break;
|
||
}
|
||
|
||
|
||
$result = self::requestUrl($request, $scene, self::$uid, $taskId);
|
||
|
||
if (!empty($result) && isset($result['id'])) {
|
||
self::$returnData = $result;
|
||
$addData = [
|
||
'user_id' => self::$uid,
|
||
'status' => $model_version == 2 ? 1 : 0,
|
||
'voice_id' => $result['id'],
|
||
'name' => $name,
|
||
'gender' => $gender,
|
||
'model_version' => $model_version,
|
||
'task_id' => $taskId,
|
||
'voice_urls' => $voice_url
|
||
];
|
||
HumanVoice::create($addData);
|
||
|
||
if(in_array( $model_version,[4,6])){
|
||
HumanTask::create([
|
||
'user_id' => self::$uid,
|
||
'video_task_id' => 0,
|
||
'task_id' => $taskId,
|
||
'model_version' => $model_version,
|
||
'data_id'=> $result['id'],
|
||
'type' => 2,
|
||
]);
|
||
}
|
||
|
||
} else {
|
||
self::setError('合成失败');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @desc 音色重试
|
||
* @param $data
|
||
* @return bool
|
||
* @throws \Exception
|
||
* @date 2024/9/28 17:46
|
||
* @author dagouzi
|
||
*/
|
||
public static function voiceRetry(int $id)
|
||
{
|
||
$voice = HumanVoice::where('id', $id)->where('user_id', self::$uid)->findOrEmpty();
|
||
if ($voice->isEmpty()) {
|
||
|
||
message('音色不存在');
|
||
}
|
||
|
||
//状态不对
|
||
if ($voice->status != 2) {
|
||
message('当前任务不是失败状态,请勿提交');
|
||
}
|
||
|
||
//提交时间间隔1分钟
|
||
if (strtotime($voice->update_time) > strtotime('-1 minute')) {
|
||
message('任务训练间隔1分钟,请勿频繁提交');
|
||
}
|
||
|
||
//保存更新时间
|
||
$voice->update_time = time();
|
||
$voice->save();
|
||
|
||
|
||
if(!empty($voice->voice_urls)){
|
||
$request = [
|
||
'name' => $voice->name,
|
||
'gender' => $voice->gender,
|
||
'audio_url' => $voice->voice_urls
|
||
];
|
||
|
||
switch ($voice->model_version) {
|
||
case 1:
|
||
$scene = self::VOICE_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::VOICE_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::VOICE_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::VOICE_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::VOICE_TRAINING_PRO;
|
||
break;
|
||
}
|
||
$result = self::requestUrl($request, $scene, $voice->user_id, $voice->task_id);
|
||
}else{
|
||
$result = [];
|
||
}
|
||
|
||
|
||
if (!empty($result) && isset($result['id'])) {
|
||
|
||
$voice->voice_id = $result['id'];
|
||
$voice->status = $voice->model_version == 2 ? 1 : 0;
|
||
|
||
if ($voice->model_version == 2) {
|
||
|
||
$voice->status = 1;
|
||
}
|
||
|
||
$voice->save();
|
||
|
||
// 更新音频
|
||
HumanAudio::where('task_id', $voice->task_id)->update([
|
||
'voice_id' => $result['id']
|
||
]);
|
||
|
||
self::$returnData = $voice->toArray();
|
||
} else {
|
||
self::setError('重试失败');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @desc 音色列表
|
||
* @param $data
|
||
* @return array
|
||
* @throws DataNotFoundException
|
||
* @throws DbException
|
||
* @throws ModelNotFoundException
|
||
* @date 2024/9/28 18:25
|
||
* @author dagouzi
|
||
*/
|
||
public static function voiceLists($data): array
|
||
{
|
||
//TODO 新增分页
|
||
$pageNo = ($data['page_no'] - 1) * $data['page_size'];
|
||
$pageSize = $data['page_size'];
|
||
$name = $data['name'] ?? '';
|
||
|
||
if (empty($data['model_version'])){
|
||
$modelVersion = '';
|
||
}else{
|
||
$modelVersion = json_decode($data['model_version'],true);
|
||
$modelVersion = is_array($modelVersion) ? $modelVersion : [(int)$modelVersion];
|
||
}
|
||
|
||
$status = $data['status'] ?? '';
|
||
$type = $data['type'] ?? 3;
|
||
$result = [];
|
||
$count = 0;
|
||
if (in_array($type,[1,3])) {
|
||
$result = HumanVoice::where(['user_id' => self::$uid])
|
||
->order('create_time', 'desc')
|
||
->limit($pageNo, $pageSize)
|
||
->when($name, function ($query) use ($name) {
|
||
$query->where('name', 'like', '%' . $name . '%');
|
||
})
|
||
->when($modelVersion, function ($query) use ($modelVersion) {
|
||
$query->where('model_version', 'in' ,$modelVersion);
|
||
})
|
||
->when($status != "", function ($query) use ($status) {
|
||
$query->where('status', $status);
|
||
})
|
||
->order('create_time', 'desc')
|
||
->select()->each(function ($item) {
|
||
$item->type = 1;
|
||
})
|
||
->toArray();
|
||
|
||
$count = HumanVoice::where(['user_id' => self::$uid])
|
||
->when($name, function ($query) use ($name) {
|
||
$query->where('name', 'like', '%' . $name . '%');
|
||
})
|
||
->when($modelVersion, function ($query) use ($modelVersion) {
|
||
$query->where('model_version', 'in' , $modelVersion);
|
||
})
|
||
->when($status != "", function ($query) use ($status) {
|
||
$query->where('status', $status);
|
||
})
|
||
->count();
|
||
}
|
||
|
||
if (in_array($type,[0,3])) {
|
||
$voice = HumanVoice::getModelList();
|
||
if ($voice) {
|
||
foreach ($voice['voice'] as &$v) {
|
||
$v['type'] = 0;
|
||
};
|
||
$result = array_merge($voice['voice'], $result);
|
||
}
|
||
$count = count( $result);
|
||
}
|
||
|
||
$data = [
|
||
'lists' => $result,
|
||
'count' => $count,
|
||
'page_no' => $data['page_no'],
|
||
'page_size' => $data['page_size'],
|
||
];
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* @desc 删除音色
|
||
* @param $data
|
||
* @return bool
|
||
* @date 2024/9/28 18:31
|
||
* @author dagouzi
|
||
*/
|
||
public static function voiceDelete($data)
|
||
{
|
||
try {
|
||
|
||
if (is_string($data['id'])) {
|
||
HumanVoice::destroy(['id' => $data['id'], 'user_id' => self::$uid]);
|
||
} else {
|
||
HumanVoice::whereIn('id', $data['id'])->where('user_id', self::$uid)->select()->delete();
|
||
}
|
||
|
||
return true;
|
||
} catch (\Exception $exception) {
|
||
self::setError($exception->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @desc 创建音频
|
||
* @param $data
|
||
* @return bool
|
||
* @date 2024/9/28 18:36
|
||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||
* @author dagouzi
|
||
*/
|
||
public static function createAudio($data)
|
||
{
|
||
// 检查余额
|
||
if ($data['model_version'] == 1) {
|
||
TokenLogService::checkToken(self::$uid, 'human_audio');
|
||
}elseif ($data['model_version'] == 4){
|
||
TokenLogService::checkToken(self::$uid, 'human_audio_ym');
|
||
}elseif ($data['model_version'] == 6){
|
||
TokenLogService::checkToken(self::$uid, 'human_audio_ymt');
|
||
}else {
|
||
TokenLogService::checkToken(self::$uid, 'human_audio_pro');
|
||
}
|
||
$msg = $data['msg'] ?? '';
|
||
$name = $data['name'] ?? '';
|
||
$voice_id = $data['voice_id'] ?? '';
|
||
$model_version = $data['model_version'] ?? '';
|
||
|
||
if (empty($msg) || !in_array($model_version, [1, 2, 4,5,6]) || empty($voice_id)) {
|
||
message('参数错误');
|
||
}
|
||
|
||
//字数超过150字节
|
||
if ($model_version == 1 && mb_strlen($msg) > 150) {
|
||
message('字数不能超过150字节');
|
||
}
|
||
|
||
if ($model_version == 2 && mb_strlen($msg) > 30000) {
|
||
message('字数不能超过30000字节');
|
||
}
|
||
|
||
$request = [
|
||
'msg' => $msg,
|
||
'voice_id' => $voice_id
|
||
];
|
||
|
||
// 检查音色ID
|
||
$voice = HumanVoice::where('voice_id', $voice_id)->findOrEmpty();
|
||
if ($voice->status != 1) {
|
||
self::setError('请等待音色创建完成');
|
||
return false;
|
||
}
|
||
|
||
$taskId = generate_unique_task_id();
|
||
|
||
switch ($model_version) {
|
||
case 1:
|
||
$scene = self::AUDIO_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::AUDIO_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::AUDIO_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::AUDIO_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::AUDIO_TRAINING_PRO;
|
||
break;
|
||
}
|
||
|
||
$result = self::requestUrl($request, $scene, self::$uid, $taskId);
|
||
|
||
if (!empty($result) && isset($result['url'])) {
|
||
self::$returnData = $result;
|
||
$addData = [
|
||
'user_id' => self::$uid,
|
||
'status' => 0,
|
||
'name' => $name,
|
||
'msg' => $msg,
|
||
'voice_id' => $voice_id,
|
||
'model_version' => $model_version,
|
||
'task_id' => $taskId,
|
||
'audio_id' => $result['id']
|
||
];
|
||
|
||
if ($model_version == 2) {
|
||
$addData['url'] = FileService::downloadFileBySource($result['url'], 'audio');
|
||
}
|
||
|
||
HumanAudio::create($addData);
|
||
} else {
|
||
self::setError('合成失败');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @desc 音频重试
|
||
* @param $data
|
||
* @return bool
|
||
* @throws \Exception
|
||
* @date 2024/9/28 17:46
|
||
* @author dagouzi
|
||
*/
|
||
public static function audioRetry(int $id)
|
||
{
|
||
$audio = HumanAudio::where('id', $id)->where('user_id', self::$uid)->findOrEmpty();
|
||
if ($audio->isEmpty()) {
|
||
|
||
message('音频不存在');
|
||
}
|
||
|
||
if (HumanVoice::where('voice_id', $audio->voice_id)->where('status', 1)->count() == 0) {
|
||
self::setError('请等待音色创建完成');
|
||
return false;
|
||
}
|
||
|
||
//状态不对
|
||
if ($audio->status != 2) {
|
||
message('当前任务不是失败状态,请勿提交');
|
||
}
|
||
|
||
//提交时间间隔1分钟
|
||
if (strtotime($audio->update_time) > strtotime('-1 minute')) {
|
||
message('任务训练间隔1分钟,请勿频繁提交');
|
||
}
|
||
|
||
//保存更新时间
|
||
$audio->update_time = time();
|
||
$audio->save();
|
||
|
||
$request = [
|
||
'msg' => $audio->msg,
|
||
'voice_id' => $audio->voice_id
|
||
];
|
||
|
||
switch ($audio->model_version)
|
||
{
|
||
case 1:
|
||
$scene = self::AUDIO_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::AUDIO_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::AUDIO_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::AUDIO_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::AUDIO_TRAINING_PRO;
|
||
break;
|
||
}
|
||
|
||
$result = self::requestUrl($request, $scene, $audio->user_id, $audio->task_id);
|
||
|
||
if ($audio->model_version == 2 && isset($result['url']) && $result['url']) {
|
||
$audio->url = FileService::downloadFileBySource($result['url'], 'audio');
|
||
$audio->status = 1;
|
||
$audio->save();
|
||
|
||
// 更新视频
|
||
HumanVideoTask::where('task_id', $audio->task_id)->update([
|
||
'audio_url' => FileService::setFileUrl($audio->url)
|
||
]);
|
||
} else if (isset($result['id']) && $result['id']) {
|
||
$audio->audio_id = $result['id'];
|
||
$audio->status = 0;
|
||
$audio->save();
|
||
} else {
|
||
self::setError('重试失败');
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
/**
|
||
* @desc 视频重试
|
||
* @param $data
|
||
* @return bool
|
||
* @throws \Exception
|
||
* @date 2024/9/28 17:46
|
||
* @author dagouzi
|
||
*/
|
||
public static function videoRetry(int $id)
|
||
{
|
||
$video = HumanVideoTask::where('id', $id)->where('user_id', self::$uid)->findOrEmpty();
|
||
if ($video->isEmpty()) {
|
||
|
||
message('视频不存在');
|
||
}
|
||
|
||
if ($video->anchor_id == "") {
|
||
self::setError('请等待形象创建完成');
|
||
return false;
|
||
}
|
||
|
||
if ($video->audio_url == "") {
|
||
self::setError('请等待音频创建完成');
|
||
return false;
|
||
}
|
||
|
||
//状态不对
|
||
if ($video->status != 2) {
|
||
message('当前任务不是失败状态,请勿提交');
|
||
}
|
||
|
||
//提交时间间隔1分钟
|
||
if (strtotime($video->update_time) > strtotime('-1 minute')) {
|
||
message('任务训练间隔1分钟,请勿频繁提交');
|
||
}
|
||
|
||
//保存更新时间
|
||
$video->update_time = time();
|
||
$video->save();
|
||
|
||
$request = [
|
||
'name' => $video->name,
|
||
'avatar_id' => $video->anchor_id,
|
||
'video_url' => $video->upload_video_url,
|
||
'audio_url' => $video->audio_url
|
||
];
|
||
|
||
switch ($video->model_version)
|
||
{
|
||
case 1:
|
||
$scene = self::VIDEO_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::VIDEO_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::VIDEO_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::VIDEO_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::VIDEO_TRAINING_PRO;
|
||
break;
|
||
}
|
||
|
||
$result = self::requestUrl($request, $scene, $video->user_id, $video->task_id);
|
||
|
||
if (!empty($result) && isset($result['id'])) {
|
||
|
||
$video->result_id = $result['id'];
|
||
$video->remark = "";
|
||
$video->status = 0;
|
||
$video->save();
|
||
self::$returnData = $video->toArray();
|
||
} else {
|
||
self::setError('重试失败');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
/**
|
||
* @desc 音频列表
|
||
* @param $data
|
||
* @return array
|
||
* @throws DataNotFoundException
|
||
* @throws DbException
|
||
* @throws ModelNotFoundException
|
||
* @date 2024/9/28 18:25
|
||
* @author dagouzi
|
||
*/
|
||
public static function audioLists($data): array
|
||
{
|
||
//TODO 新增分页
|
||
$pageNo = ($data['page_no'] - 1) * $data['page_size'];
|
||
$pageSize = $data['page_size'];
|
||
$name = $data['name'] ?? '';
|
||
$modelVersion = $data['model_version'] ?? '';
|
||
$status = $data['status'] ?? '';
|
||
$result = HumanAudio::where(['user_id' => self::$uid])
|
||
->order('create_time', 'desc')
|
||
->limit($pageNo, $pageSize)
|
||
->when($name, function ($query) use ($name) {
|
||
$query->where('name', 'like', '%' . $name . '%');
|
||
})
|
||
->when($modelVersion, function ($query) use ($modelVersion) {
|
||
$query->where('model_version', $modelVersion);
|
||
})
|
||
->when($status != "", function ($query) use ($status) {
|
||
$query->where('status', $status);
|
||
})
|
||
->order('create_time', 'desc')
|
||
->select()
|
||
->toArray();
|
||
|
||
$data = [
|
||
'lists' => $result,
|
||
'count' => HumanAudio::where(['user_id' => self::$uid])
|
||
->when($modelVersion, function ($query) use ($modelVersion) {
|
||
$query->where('model_version', $modelVersion);
|
||
})
|
||
->count(),
|
||
'page_no' => $data['page_no'],
|
||
'page_size' => $data['page_size'],
|
||
];
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* @desc 删除音频
|
||
* @param $data
|
||
* @return bool
|
||
* @date 2024/9/28 18:31
|
||
* @author dagouzi
|
||
*/
|
||
public static function audioDelete($data)
|
||
{
|
||
try {
|
||
|
||
if (is_string($data['id'])) {
|
||
HumanAudio::destroy(['id' => $data['id'], 'user_id' => self::$uid]);
|
||
} else {
|
||
HumanAudio::whereIn('id', $data['id'])->where('user_id', self::$uid)->select()->delete();
|
||
}
|
||
|
||
return true;
|
||
} catch (\Exception $exception) {
|
||
self::setError($exception->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @desc 视频列表
|
||
* @return array
|
||
* @date 2024/9/28 18:25
|
||
* @throws \think\db\exception\DataNotFoundException
|
||
* @throws \think\db\exception\DbException
|
||
* @throws \think\db\exception\ModelNotFoundException
|
||
* @author dagouzi
|
||
*/
|
||
public static function videoLists(array $data)
|
||
{
|
||
$modelVersion = $data['model_version'] ?? '';
|
||
$name = $data['name'] ?? '';
|
||
$status = $data['status'] ?? '';
|
||
$pageNo = ($data['page_no'] - 1) * $data['page_size'];
|
||
$pageSize = $data['page_size'];
|
||
$result = HumanVideoTask::where(['user_id' => self::$uid])
|
||
->when($modelVersion, function ($query) use ($modelVersion) {
|
||
$query->where('model_version', $modelVersion);
|
||
})
|
||
->when($name, function ($query) use ($name) {
|
||
$query->where('name', 'like', '%' . $name . '%');
|
||
})
|
||
->when($status != "", function ($query) use ($status) {
|
||
$query->where('status', $status);
|
||
})
|
||
->order('create_time', 'desc')
|
||
->limit($pageNo, $pageSize)
|
||
->select()
|
||
->toArray();
|
||
|
||
$data = [
|
||
'lists' => $result,
|
||
'count' => HumanVideoTask::where(['user_id' => self::$uid])
|
||
->when($modelVersion, function ($query) use ($modelVersion) {
|
||
$query->where('model_version', $modelVersion);
|
||
})
|
||
->when($name, function ($query) use ($name) {
|
||
$query->where('name', 'like', '%' . $name . '%');
|
||
})
|
||
->count(),
|
||
'page_no' => $data['page_no'],
|
||
'page_size' => $data['page_size'],
|
||
];
|
||
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* @desc 删除视频
|
||
* @param $data
|
||
* @return bool
|
||
* @date 2024/9/28 18:31
|
||
* @author dagouzi
|
||
*/
|
||
public static function videoDelete($data)
|
||
{
|
||
try {
|
||
|
||
if (is_string($data['id'])) {
|
||
HumanVideoTask::destroy(['id' => $data['id'], 'user_id' => self::$uid]);
|
||
} else {
|
||
HumanVideoTask::whereIn('id', $data['id'])->where('user_id', self::$uid)->select()->delete();
|
||
}
|
||
|
||
return true;
|
||
} catch (\Exception $exception) {
|
||
self::setError($exception->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @desc 视频信息定时任务
|
||
* @return bool
|
||
*/
|
||
public static function videoInfoCron(): bool
|
||
{
|
||
|
||
try {
|
||
HumanVideoTask::where('status', 0)
|
||
->where('result_id', '<>', '')
|
||
->where('model_version', '=', 2)
|
||
->order('tries', 'asc')
|
||
->limit(3)
|
||
->select()
|
||
->each(function ($item) {
|
||
try {
|
||
$response = \app\common\service\ToolsService::Human()->detailPro([
|
||
'id' => $item['result_id']
|
||
]);
|
||
//阿里极速版 2
|
||
// Log::write('阿里获取视频结果' . json_encode($response));
|
||
if (!empty($response['data']['url'])) {
|
||
$item->status = 1;
|
||
$item->tries = $item['tries'] + 1;
|
||
$item->result_url = FileService::downloadFileBySource($response['data']['url'], 'video');
|
||
$item->save();
|
||
return true;
|
||
}else{
|
||
$item->tries = $item['tries'] + 1;
|
||
$item->save();
|
||
return true;
|
||
}
|
||
} catch (\think\exception\HttpResponseException $e) {
|
||
Log::write('数字人视频保存失败' .$item['tries'].'----' . $e->getResponse()->getData()['msg']);
|
||
$item->remark = $e->getResponse()->getData()['msg'] ?? '';
|
||
$item->tries = $item['tries'] + 1;
|
||
$item->status = 2;
|
||
$item->save();
|
||
//退费
|
||
//查询是否已返还
|
||
if (UserTokensLog::where('user_id', $item->user_id)->where('change_type', AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_PRO)->where('action', 1)->where('task_id', $item->task_id)->count() == 0) {
|
||
|
||
$points = UserTokensLog::where('user_id', $item->user_id)->where('change_type', AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_PRO)->where('task_id', $item->task_id)->value('change_amount') ?? 0;
|
||
|
||
AccountLogLogic::recordUserTokensLog(false, $item->user_id, AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_PRO, $points, $item->task_id);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
return true;
|
||
});
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
Log::write('视频信息定时任务失败' . $e->getMessage());
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @desc 视频定时任务
|
||
* @return bool|void
|
||
* @date 2024/10/2 10:36
|
||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||
* @throws \think\db\exception\DataNotFoundException
|
||
* @throws \think\db\exception\DbException
|
||
* @throws \think\db\exception\ModelNotFoundException
|
||
* @author dagouzi
|
||
*/
|
||
public static function videoTaskCron(string $taskId = '')
|
||
{
|
||
try {
|
||
if ($taskId == '') {
|
||
// 请求30分钟内,还处于0状态的任务
|
||
HumanVideoTask::where('status', 0)
|
||
->where('result_id', '')
|
||
->where('create_time', '<=', strtotime('-30 minutes'))
|
||
->select()
|
||
->each(function ($item) {
|
||
self::setTimeoutTask($item->task_id);
|
||
});
|
||
}
|
||
// 第一步:获取任务列表
|
||
$taskModel = HumanVideoTask::where('status', 0)
|
||
->where('result_id', '')
|
||
->where('tries', '<', 3)
|
||
->limit(3);
|
||
|
||
|
||
if ($taskId) {
|
||
|
||
$taskModel = $taskModel->where('task_id', $taskId);
|
||
}
|
||
|
||
$modellist = HumanVoice::getModelList();
|
||
|
||
|
||
//第二步遍历任务
|
||
$taskModel->select()->each(function ($item) {
|
||
// var_dump('运行');
|
||
// //如果存在失败,且还没到执行时间(1分钟后执行)
|
||
if ($item->tries >= 1 && strtotime($item->update_time) > strtotime('-1 minute')) {
|
||
|
||
return true;
|
||
}
|
||
try {
|
||
$tries = $item->tries;
|
||
//step1 形象
|
||
$anchor = HumanAnchor::where('user_id', $item['user_id'])->where('anchor_id', $item->anchor_id)->findOrEmpty();
|
||
// 如果形象不存在,则创建形象
|
||
if ($anchor->isEmpty()) {
|
||
|
||
$anchor = HumanAnchor::create([
|
||
'task_id' => $item->task_id,
|
||
'model_version' => $item->model_version,
|
||
'name' => $item->anchor_name,
|
||
'gender' => $item->gender,
|
||
'status' => 0,
|
||
'pic' => $item->pic,
|
||
'url' => $item->upload_video_url,
|
||
'user_id' => $item['user_id']
|
||
]);
|
||
}
|
||
|
||
// var_dump('人像训练');
|
||
// 如果没有训练,请求训练
|
||
if ($anchor->anchor_id == ""){
|
||
// var_dump('开始训练');
|
||
switch ($item->model_version) {
|
||
case 1:
|
||
$scene = self::AVATAR_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::AVATAR_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::AVATAR_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::AVATAR_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::AVATAR_TRAINING_PRO;
|
||
break;
|
||
}
|
||
//请求形象接口
|
||
$response = self::requestUrl([
|
||
'name' => $anchor->name,
|
||
'gender' => $anchor->gender,
|
||
'video_url' => $anchor->url,
|
||
], $scene, $item->user_id, $item->task_id);
|
||
// var_dump( $response);
|
||
if (empty($response['id'])) {
|
||
|
||
message('形象创建失败');
|
||
}
|
||
|
||
if (in_array($item->model_version,[2,4,6])) {
|
||
|
||
$anchor->status = 1;
|
||
}
|
||
|
||
$anchor->anchor_id = $response['id'];
|
||
$anchor->save();
|
||
|
||
// 保存形象id
|
||
$item->anchor_id = $anchor->anchor_id;
|
||
$item->save();
|
||
}
|
||
// var_dump('请求完成');
|
||
// 形象还没有训练完成
|
||
if ($anchor->status != 1) {
|
||
|
||
return true;
|
||
}
|
||
|
||
// 文案驱动
|
||
if ($item->audio_type == 1) {
|
||
//step2 音色
|
||
$voice = HumanVoice::where('user_id', $item['user_id'])->where('voice_id', $item->voice_id)->findOrEmpty();
|
||
|
||
$voiceres = false;
|
||
// 如果音色不存在,则创建音色
|
||
if ($voice->isEmpty()) {
|
||
$BuiltInVoice = HumanVoice::getBuiltInVoiceList($item->model_version);
|
||
if (!in_array($item->voice_id, $BuiltInVoice)) {
|
||
$voice = HumanVoice::create([
|
||
'task_id' => $item->task_id,
|
||
'model_version' => $item->model_version,
|
||
'name' => $item->voice_name ? $item->voice_name : $item->name,
|
||
'gender' => $item->gender,
|
||
'voice_urls' => $item->upload_video_url,
|
||
'user_id' => $item['user_id']
|
||
]);
|
||
$voiceres = false;
|
||
|
||
}else{
|
||
$voice->voice_id = $item->voice_id;
|
||
$voiceres = true;
|
||
}
|
||
|
||
|
||
}
|
||
// var_dump('ys开始');
|
||
// 如果没有训练,请求训练
|
||
if ($voice->voice_id == "" && !$voiceres) {
|
||
// var_dump('创建音色');
|
||
switch ($item->model_version) {
|
||
case 1:
|
||
$scene = self::VOICE_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::VOICE_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::VOICE_TRAINING_YM;
|
||
$item->upload_video_url = FileService::getFileUrl($item->upload_video_url);
|
||
// var_dump( $item->upload_video_url );
|
||
break;
|
||
case 6:
|
||
$scene = self::VOICE_TRAINING_YMT;
|
||
$item->upload_video_url = FileService::getFileUrl($item->upload_video_url);
|
||
break;
|
||
default:
|
||
$scene = self::VOICE_TRAINING_PRO;
|
||
break;
|
||
}
|
||
$response = self::requestUrl([
|
||
'name' => $voice->name,
|
||
'gender' => $voice->gender,
|
||
'is_video' => true,
|
||
'audio_url' => $item->upload_video_url
|
||
], $scene, $item->user_id, $item->task_id);
|
||
// var_dump( $response );
|
||
if (empty($response['id'])) {
|
||
|
||
message('音色创建失败');
|
||
}
|
||
|
||
if ($item->model_version == 2) {
|
||
|
||
$voice->status = 1;
|
||
}
|
||
|
||
$voice->voice_id = $response['id'];
|
||
$voice->save();
|
||
|
||
$item->voice_id = $response['id'];
|
||
$item->save();
|
||
|
||
if(in_array($item->model_version,[4,6])){
|
||
$humantask = HumanTask::where([
|
||
'user_id'=> $item['user_id'],
|
||
'video_task_id' => $item->id,
|
||
'task_id' => $item->task_id,
|
||
'type' => 2,
|
||
'model_version' => $item->model_version,
|
||
])->findOrEmpty();
|
||
|
||
// 如果音色不存在,则创建音色
|
||
if ($humantask->isEmpty()) {
|
||
|
||
HumanTask::create([
|
||
'user_id' => $item['user_id'],
|
||
'video_task_id' => $item->id,
|
||
'task_id' => $item->task_id,
|
||
'model_version' => $item->model_version,
|
||
'data_id'=> $response['id'],
|
||
'type' => 2,
|
||
]);
|
||
|
||
$item->tries = 6;
|
||
$item->save();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 音色还没有训练完成
|
||
if ($voice->status != 1 && !$voiceres) {
|
||
|
||
return true;
|
||
}
|
||
// var_dump('创建音频');
|
||
|
||
//step3 音频
|
||
$audio = HumanAudio::where('user_id', $item['user_id'])->where('task_id', $item->task_id)->findOrEmpty();
|
||
|
||
// 如果音频不存在,则创建音频
|
||
if ($audio->isEmpty()) {
|
||
|
||
$audio = HumanAudio::create([
|
||
'user_id' => $item['user_id'],
|
||
'task_id' => $item->task_id,
|
||
'model_version' => $item->model_version,
|
||
'name' => $item->name,
|
||
'msg' => $item->msg,
|
||
'voice_id' => $voice->voice_id
|
||
]);
|
||
}
|
||
|
||
// 如果没有训练,请求训练
|
||
if ($audio->audio_id == "") {
|
||
// var_dump('训练音频');
|
||
switch ($item->model_version) {
|
||
case 1:
|
||
$scene = self::AUDIO_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::AUDIO_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::AUDIO_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::AUDIO_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::AUDIO_TRAINING_PRO;
|
||
break;
|
||
}
|
||
// 请求合成音频
|
||
$response = self::requestUrl([
|
||
'msg' => $item->msg,
|
||
'voice_id' => $voice->voice_id
|
||
], $scene, $item->user_id, $item->task_id);
|
||
//var_dump( $response );
|
||
// Log::write( $item->task_id .'高级版音频合成' . json_encode($response));
|
||
if ($item->model_version == 2) {
|
||
|
||
if (empty($response['url'])) {
|
||
|
||
message('音频创建失败');
|
||
}
|
||
|
||
$audio->audio_id = uniqid();
|
||
$audio->url = FileService::downloadFileBySource($response['url'], 'audio');
|
||
$audio->status = 1;
|
||
$audio->save();
|
||
} elseif(in_array($item->model_version,[4,6])){
|
||
if (empty($response['id'])) {
|
||
|
||
message('音频创建失败');
|
||
}
|
||
|
||
$audio->audio_id = $response['id'];
|
||
$audio->status = 0;
|
||
$audio->save();
|
||
$humantask = HumanTask::where([
|
||
'task_id' => $item->task_id,
|
||
'video_task_id' => $item->id,
|
||
'model_version' => $item->model_version,
|
||
'type' => 3,
|
||
'user_id'=> $item['user_id'],
|
||
])->findOrEmpty();
|
||
|
||
|
||
// var_dump('音频定时任务'.$humantask->isEmpty());
|
||
// 如果声音不存在,则创建
|
||
if ($humantask->isEmpty()) {
|
||
|
||
HumanTask::create([
|
||
'task_id' => $item->task_id,
|
||
'video_task_id' => $item->id,
|
||
'model_version' => $item->model_version,
|
||
'data_id'=> $response['id'],
|
||
'type' => 3,
|
||
'user_id' => $item['user_id']
|
||
]);
|
||
|
||
$item->tries = 6;
|
||
$item->save();
|
||
}
|
||
}else {
|
||
|
||
if (empty($response['id'])) {
|
||
|
||
message('音频创建失败');
|
||
}
|
||
|
||
$audio->audio_id = $response['id'];
|
||
$audio->save();
|
||
}
|
||
}
|
||
|
||
// 音频还没有训练完成
|
||
if ($audio->status != 1) {
|
||
|
||
return true;
|
||
}
|
||
|
||
// 保存音频url
|
||
$item->audio_url = $audio->url;
|
||
$item->save();
|
||
}
|
||
|
||
|
||
// var_dump('视频开始合成');
|
||
//最终合成视频 有形象了,有音频了
|
||
if ($item->anchor_id != "" && $item->audio_url != "") {
|
||
|
||
switch ($item->model_version) {
|
||
case 1:
|
||
$scene = self::VIDEO_TRAINING;
|
||
break;
|
||
case 2:
|
||
$scene = self::VIDEO_TRAINING_PRO;
|
||
break;
|
||
case 4:
|
||
$scene = self::VIDEO_TRAINING_YM;
|
||
break;
|
||
case 6:
|
||
$scene = self::VIDEO_TRAINING_YMT;
|
||
break;
|
||
default:
|
||
$scene = self::VIDEO_TRAINING_PRO;
|
||
break;
|
||
}
|
||
|
||
$response = self::requestUrl([
|
||
'name' => $item->name,
|
||
'avatar_id' => $item->anchor_id,
|
||
'video_url' => $item->upload_video_url,
|
||
'audio_url' => $item->audio_url
|
||
], $scene, $item->user_id, $item->task_id);
|
||
// Log::write( $item->task_id .'高级版视频合成' . json_encode($response));
|
||
if (empty($response['id'])) {
|
||
|
||
message('视频创建失败');
|
||
}
|
||
|
||
$item->result_id = $response['id'];
|
||
$item->save();
|
||
}
|
||
|
||
return true;
|
||
} catch (\think\exception\HttpResponseException $e) {
|
||
$item->tries = $tries + 1;
|
||
$item->save();
|
||
//失败3次 更新任务失败
|
||
if ($item->tries >= 3) {
|
||
$remark = $e->getResponse()->getData()['msg'] ?? '';
|
||
self::updateFailTask($item->task_id, $remark);
|
||
}
|
||
return true;
|
||
}
|
||
});
|
||
} catch (\Exception $e) {
|
||
Log::channel('human')->write('数字人错误:'.$e->getMessage());
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @desc 数字人定时任务
|
||
* @return bool
|
||
*/
|
||
public static function humanInfoCron(): bool
|
||
{
|
||
try {
|
||
HumanTask::where('status', 0)
|
||
->whereIn('model_version',[4,6])
|
||
->where('tries', '<', 8)
|
||
->order('tries', 'asc')
|
||
->limit(3)
|
||
->select()
|
||
->each(function ($item) {
|
||
try {
|
||
$methodMap = [
|
||
4 => 'detailYm',
|
||
6 => 'detailYmt',
|
||
];
|
||
|
||
$method = $methodMap[$item['model_version']] ?? 'detailYmt';
|
||
if($item['type'] == 2){
|
||
// var_dump('获取音色'. $item['task_id']);
|
||
|
||
$response = \app\common\service\ToolsService::Human()->$method([
|
||
'type' => $item['type'],
|
||
'id' => $item['data_id']
|
||
]);
|
||
// Log::write('高级版获取音色结果' . json_encode($response));
|
||
if (isset($response['data']['status']) && $response['data']['status'] == 3) {
|
||
$item->result_id = $response['data']['train_id'];
|
||
$item->status = 1;
|
||
$item->tries = $item['tries'] + 1;
|
||
$item->result_url = $response['data']['demo_audio'];
|
||
$item->upload_url = FileService::downloadFileBySource($response['data']['demo_audio'], 'audio');
|
||
$item->save();
|
||
|
||
$task = HumanVoice::where([
|
||
'user_id'=> $item['user_id'],
|
||
'task_id'=> $item['task_id'],
|
||
'status' =>0
|
||
])->update([
|
||
'status' =>1,
|
||
'voice_urls'=> $item->upload_url
|
||
]);
|
||
// var_dump('音色更新'. $task);
|
||
$tt = HumanVideoTask::where([
|
||
'user_id'=> $item['user_id'],
|
||
'task_id'=> $item['task_id'],
|
||
'status' =>0
|
||
])->update([
|
||
'tries'=> 1
|
||
]);
|
||
// var_dump('任务重组'.$tt);
|
||
return true;
|
||
}else{
|
||
$item->tries = $item['tries'] + 1;
|
||
$item->save();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
if($item['type'] == 3){
|
||
// var_dump('获取声音合成结果'. $item['task_id']);
|
||
$response = \app\common\service\ToolsService::Human()->$method([
|
||
'type' => $item['type'],
|
||
'id' => $item['data_id']
|
||
]);
|
||
// var_dump( $response);
|
||
//阿里极速版 2
|
||
if (isset($response['data']['status']) && $response['data']['status'] == 3) {
|
||
$upload_url = FileService::downloadFileBySource($response['data']['speech_url'], 'audio');
|
||
$item->result_id = $response['data']['id'];
|
||
$item->status = 1;
|
||
$item->tries = $item['tries'] + 1;
|
||
$item->result_url = $response['data']['speech_url'];
|
||
$item->upload_url = $upload_url;
|
||
$item->save();
|
||
|
||
$task = HumanAudio::where([
|
||
'user_id'=> $item['user_id'],
|
||
'task_id'=> $item['task_id'],
|
||
'status' =>0,
|
||
'audio_id'=> $response['data']['task_id'],
|
||
])->update([
|
||
'status' =>1,
|
||
'url' => $upload_url
|
||
]);
|
||
//var_dump('音色更新'. $task);
|
||
$tt = HumanVideoTask::where([
|
||
'user_id'=> $item['user_id'],
|
||
'task_id'=> $item['task_id'],
|
||
'status' =>0
|
||
])->update([
|
||
'tries'=> 1,
|
||
'audio_url' => $upload_url
|
||
|
||
]);
|
||
// var_dump('任务重组'.$tt);
|
||
return true;
|
||
}else{
|
||
$item->tries = $item['tries'] + 1;
|
||
$item->save();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
} catch (\think\exception\HttpResponseException $e) {
|
||
Log::write('数字人保存失败' .$item['tries'].'----' . $e->getResponse()->getData()['msg']);
|
||
$item->remark = $e->getResponse()->getData()['msg'] ?? '';
|
||
$item->tries = $item['tries'] + 1;
|
||
$item->status = 2;
|
||
$item->save();
|
||
if($item->type == 2){
|
||
if($item->model_version == 4){
|
||
$scene = AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_YM;
|
||
}else{
|
||
$scene = AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_YMT;
|
||
}
|
||
//查询是否已返还
|
||
if (UserTokensLog::where('user_id', $item->user_id)->where('change_type', $scene)->where('action', 1)->where('task_id', $item->task_id)->count() == 0) {
|
||
|
||
$points = UserTokensLog::where('user_id', $item->user_id)->where('change_type', $scene)->where('task_id', $item->task_id)->value('change_amount') ?? 0;
|
||
|
||
AccountLogLogic::recordUserTokensLog(false, $item->user_id, $scene, $points, $item->task_id);
|
||
}
|
||
}else{
|
||
if($item->model_version == 4){
|
||
$scene = AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YM;
|
||
}else{
|
||
$scene = AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YMT;
|
||
}
|
||
if (UserTokensLog::where('user_id', $item->user_id)->where('change_type', $scene)->where('action', 1)->where('task_id', $item->task_id)->count() == 0) {
|
||
|
||
$points = UserTokensLog::where('user_id', $item->user_id)->where('change_type', $scene)->where('task_id', $item->task_id)->value('change_amount') ?? 0;
|
||
|
||
AccountLogLogic::recordUserTokensLog(false, $item->user_id, $scene, $points, $item->task_id);
|
||
}
|
||
}
|
||
//退费
|
||
|
||
|
||
return true;
|
||
}
|
||
return true;
|
||
});
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
Log::write('视频信息定时任务失败' . $e->getMessage());
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @desc 删除失败任务
|
||
* @param $taskId
|
||
* @return void
|
||
* @date 2024/10/2 10:43
|
||
* @author dagouzi
|
||
*/
|
||
private static function deleteFailTask(string $taskId): void
|
||
{
|
||
//删除音频失败任务, status=0,2
|
||
// TODO 不删除 让用户手动重试
|
||
// HumanAudio::where('task_id', $taskId)->whereIn('status', [0, 2])->findOrEmpty()->delete();
|
||
|
||
// //删除音色失败任务, status=0,2
|
||
// HumanVoice::where('task_id', $taskId)->whereIn('status', [0, 2])->findOrEmpty()->delete();
|
||
|
||
// //删除形象失败任务, status=0,2
|
||
// HumanAnchor::where('task_id', $taskId)->whereIn('status', [0, 2])->findOrEmpty()->delete();
|
||
|
||
// //删除视频失败任务, status=0,2
|
||
// HumanVideoTask::where('task_id', $taskId)->whereIn('status', [0, 2])->findOrEmpty()->delete();
|
||
}
|
||
|
||
|
||
/**
|
||
* @desc 删除失败任务
|
||
* @param string $taskId
|
||
* @param string $remark
|
||
* @return void
|
||
* @date 2024/10/2 10:43
|
||
* @author dagouzi
|
||
*/
|
||
private static function updateFailTask(string $taskId, string $remark): void
|
||
{
|
||
//更新音频失败任务, status=0,2
|
||
HumanAudio::where('task_id', $taskId)->where('status', 0)->update([
|
||
'status' => 2,
|
||
]);
|
||
|
||
//更新音色失败任务, status=0,2
|
||
HumanVoice::where('task_id', $taskId)->where('status', 0)->update([
|
||
'status' => 2,
|
||
]);
|
||
|
||
//更新形象失败任务, status=0,2
|
||
HumanAnchor::where('task_id', $taskId)->where('status', 0)->update([
|
||
'status' => 2,
|
||
]);
|
||
|
||
//更新视频失败任务, status=0,2
|
||
HumanVideoTask::where('task_id', $taskId)->where('status', 0)->update([
|
||
'status' => 2,
|
||
'remark' => $remark
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* @desc 超时任务
|
||
* @param $taskId
|
||
* @return void
|
||
* @date 2024/10/2 10:43
|
||
* @author dagouzi
|
||
*/
|
||
private static function setTimeoutTask(string $taskId): void
|
||
{
|
||
$item = HumanVideoTask::where('task_id', $taskId)->findOrEmpty()->toArray();
|
||
|
||
// 任务超时
|
||
$Audio = HumanAudio::where('task_id', $taskId)->whereIn('status', 0)->update(['status' => 2]);
|
||
if ($Audio) {
|
||
switch ($item['model_version'])
|
||
{
|
||
case 1:
|
||
$scene = 'human_audio';
|
||
break;
|
||
case 2:
|
||
$scene = 'human_audio_pro';
|
||
break;
|
||
case 4:
|
||
$scene = 'human_audio_ym';
|
||
break;
|
||
case 6:
|
||
$scene = 'human_audio_ymt';
|
||
break;
|
||
default:
|
||
$scene = 'human_audio';
|
||
break;
|
||
}
|
||
self::refundTokens($item['user_id'], $item['audio_id'], $item['task_id'], $scene);
|
||
}
|
||
|
||
$Voice = HumanVoice::where('task_id', $taskId)->whereIn('status', 0)->update(['status' => 2]);
|
||
if ($Voice) {
|
||
switch ($item['model_version'])
|
||
{
|
||
case 1:
|
||
$scene = 'human_voice';
|
||
break;
|
||
case 2:
|
||
$scene = 'human_video_pro';
|
||
break;
|
||
case 4:
|
||
$scene = 'human_video_ym';
|
||
break;
|
||
case 6:
|
||
$scene = 'human_video_ymt';
|
||
break;
|
||
default:
|
||
$scene = 'human_video';
|
||
break;
|
||
}
|
||
self::refundTokens($item['user_id'], $item['voice_id'], $item['task_id'], $scene);
|
||
|
||
}
|
||
|
||
$Anchor = HumanAnchor::where('task_id', $taskId)->whereIn('status', 0)->update(['status' => 2]);
|
||
|
||
|
||
$Video = HumanVideoTask::where('task_id', $taskId)->whereIn('status', 0)->update(['status' => 2, 'remark' => '创作超时']);
|
||
if (!$Audio && !$Voice && !$Anchor && $Video) {
|
||
switch ($item['model_version'])
|
||
{
|
||
case 1:
|
||
$scene = 'human_video';
|
||
break;
|
||
case 2:
|
||
$scene = 'human_video_pro';
|
||
break;
|
||
case 4:
|
||
$scene = 'human_video_ym';
|
||
break;
|
||
case 6:
|
||
$scene = 'human_video_ymt';
|
||
break;
|
||
default:
|
||
$scene = 'human_video';
|
||
break;
|
||
}
|
||
self::refundTokens($item['user_id'], $item['result_id'], $item['task_id'], $scene);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 请求上游接口与计费
|
||
* @param array $request
|
||
* @param string $scene
|
||
* @param int $userId
|
||
* @param string $taskId
|
||
* @return array
|
||
* @throws \Exception
|
||
*/
|
||
private static function requestUrl(array $request, string $scene, int $userId, string $taskId): array
|
||
{
|
||
|
||
$requestService = \app\common\service\ToolsService::Human();
|
||
|
||
[$tokenScene, $tokenCode] = match ($scene) {
|
||
self::AUDIO_TRAINING => ['human_audio', AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO],
|
||
self::AVATAR_TRAINING => ['human_avatar', AccountLogEnum::TOKENS_DEC_HUMAN_AVATAR],
|
||
self::VOICE_TRAINING => ['human_voice', AccountLogEnum::TOKENS_DEC_HUMAN_VOICE],
|
||
self::VIDEO_TRAINING => ['human_video', AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO],
|
||
self::COPYWRITING_CREATE => ['copywriting_create', AccountLogEnum::TOKENS_DEC_HUMAN_COPYWRITING],
|
||
self::AUDIO_TRAINING_PRO => ['human_audio_pro', AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_PRO],
|
||
self::AVATAR_TRAINING_PRO => ['human_avatar_pro', AccountLogEnum::TOKENS_DEC_HUMAN_AVATAR_PRO],
|
||
self::VOICE_TRAINING_PRO => ['human_voice_pro', AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_PRO],
|
||
self::VIDEO_TRAINING_PRO => ['human_video_pro', AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_PRO],
|
||
self::AUDIO_TRAINING_YM => ['human_audio_ym', AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YM],
|
||
self::AVATAR_TRAINING_YM => ['human_avatar_ym', AccountLogEnum::TOKENS_DEC_HUMAN_AVATAR_YM],
|
||
self::VOICE_TRAINING_YM => ['human_voice_ym', AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_YM],
|
||
self::VIDEO_TRAINING_YM => ['human_video_ym', AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_YM],
|
||
self::AUDIO_TRAINING_YMT => ['human_audio_ymt', AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YMT],
|
||
self::AVATAR_TRAINING_YMT => ['human_avatar_ymt', AccountLogEnum::TOKENS_DEC_HUMAN_AVATAR_YMT],
|
||
self::VOICE_TRAINING_YMT => ['human_voice_ymt', AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_YMT],
|
||
self::VIDEO_TRAINING_YMT => ['human_video_ymt', AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_YMT],
|
||
};
|
||
|
||
//计费
|
||
$unit = TokenLogService::checkToken($userId, $tokenScene);
|
||
|
||
// 添加辅助参数
|
||
$request['task_id'] = $taskId;
|
||
$request['user_id'] = $userId;
|
||
$request['now'] = time();
|
||
switch ($scene) {
|
||
|
||
case self::AVATAR_TRAINING:
|
||
|
||
$response = $requestService->avatarTraining($request);
|
||
break;
|
||
|
||
case self::VOICE_TRAINING:
|
||
|
||
$response = $requestService->voiceTraining($request);
|
||
break;
|
||
|
||
case self::AUDIO_TRAINING:
|
||
|
||
$response = $requestService->audioTraining($request);
|
||
break;
|
||
|
||
case self::VIDEO_TRAINING:
|
||
|
||
$response = $requestService->videoTraining($request);
|
||
break;
|
||
case self::COPYWRITING_CREATE:
|
||
|
||
$response = $requestService->copywritingCreate($request);
|
||
break;
|
||
case self::AVATAR_TRAINING_PRO:
|
||
|
||
$response = $requestService->avatarTrainingPro($request);
|
||
break;
|
||
case self::VOICE_TRAINING_PRO:
|
||
|
||
$response = $requestService->voiceTrainingPro($request);
|
||
break;
|
||
case self::AUDIO_TRAINING_PRO:
|
||
|
||
$response = $requestService->audioTrainingPro($request);
|
||
break;
|
||
case self::VIDEO_TRAINING_PRO:
|
||
|
||
$response = $requestService->videoTrainingPro($request);
|
||
break;
|
||
|
||
case self::AVATAR_TRAINING_YM:
|
||
$response = $requestService->avatarTrainingYm($request);
|
||
// var_dump( $response);
|
||
break;
|
||
case self::VOICE_TRAINING_YM:
|
||
$response = $requestService->voiceTrainingYm($request);
|
||
break;
|
||
case self::AUDIO_TRAINING_YM:
|
||
|
||
$response = $requestService->audioTrainingYm($request);
|
||
|
||
break;
|
||
case self::VIDEO_TRAINING_YM:
|
||
$response = $requestService->videoTrainingYm($request);
|
||
break;
|
||
case self::AVATAR_TRAINING_YMT:
|
||
$response = $requestService->avatarTrainingYmt($request);
|
||
break;
|
||
case self::VOICE_TRAINING_YMT:
|
||
$response = $requestService->voiceTrainingYmt($request);
|
||
break;
|
||
case self::AUDIO_TRAINING_YMT:
|
||
$response = $requestService->audioTrainingYmt($request);
|
||
break;
|
||
case self::VIDEO_TRAINING_YMT:
|
||
$response = $requestService->videoTrainingYmt($request);
|
||
break;
|
||
default:
|
||
}
|
||
//成功响应,需要扣费
|
||
if (isset($response['code']) && $response['code'] == 10000) {
|
||
|
||
$points = $unit;
|
||
|
||
if ($points > 0) {
|
||
|
||
$extra = [];
|
||
|
||
//合成视频按时长扣费
|
||
if (in_array($scene, [
|
||
self::AUDIO_TRAINING, self::VIDEO_TRAINING,
|
||
self::AUDIO_TRAINING_PRO, self::VIDEO_TRAINING_PRO,
|
||
self::AUDIO_TRAINING_YM, self::VIDEO_TRAINING_YM,
|
||
self::AUDIO_TRAINING_YMT, self::VIDEO_TRAINING_YMT
|
||
])) {
|
||
|
||
$duration = $response['data']['duration'] ?? 1;
|
||
|
||
$points = ceil($duration * $unit);
|
||
|
||
$extra = ['音视频时长' => $duration, '算力单价' => $unit, '实际消耗算力' => $points];
|
||
}
|
||
|
||
//token扣除
|
||
User::userTokensChange($userId, $points);
|
||
|
||
//记录日志
|
||
AccountLogLogic::recordUserTokensLog(true, $userId, $tokenCode, $points, $taskId, $extra);
|
||
}
|
||
}
|
||
|
||
return $response['data'] ?? [];
|
||
}
|
||
|
||
/**
|
||
* 请求上游接口与计费
|
||
* @param array $request
|
||
* @param string $scene
|
||
* @param int $userId
|
||
* @param string $taskId
|
||
* @return array
|
||
* @throws \Exception
|
||
*/
|
||
|
||
private static function tokeOutToken(string $scene, int $userId, string $taskId, string $id , $type = 0, $response = []): bool
|
||
{
|
||
|
||
$requestService = \app\common\service\ToolsService::Human();
|
||
|
||
[$tokenScene, $tokenCode] = match ($scene) {
|
||
self::AUDIO_TRAINING_YM => ['human_audio_ym', AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YM],
|
||
self::VOICE_TRAINING_YM => ['human_voice_ym', AccountLogEnum::TOKENS_DEC_HUMAN_VOICE_YM],
|
||
self::VIDEO_TRAINING_YM => ['human_video_ym', AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_YM],
|
||
};
|
||
|
||
//计费
|
||
$unit = TokenLogService::checkToken($userId, $tokenScene);
|
||
|
||
// 添加辅助参数
|
||
$request['task_id'] = $taskId;
|
||
$request['user_id'] = $userId;
|
||
$request['id'] = $id;
|
||
$request['type'] = $type;
|
||
$request['now'] = time();
|
||
|
||
if($response == []){
|
||
switch ($scene) {
|
||
|
||
case self::VOICE_TRAINING_YM:
|
||
$response = $requestService->detailYm($request);
|
||
break;
|
||
case self::AUDIO_TRAINING_YM:
|
||
$response = $requestService->detailYm($request);
|
||
break;
|
||
case self::VIDEO_TRAINING_YM:
|
||
$response = $requestService->detailYm($request);
|
||
break;
|
||
default:
|
||
}
|
||
}
|
||
|
||
//成功响应,需要扣费
|
||
if (isset($response['code']) && $response['code'] == 10000) {
|
||
if($scene == self::VIDEO_TRAINING_YM){
|
||
$response['data']['status'] = $response['data']['task_status'] ?? 0;
|
||
}
|
||
|
||
$points = $unit;
|
||
if ($points > 0 && $response['data']['status'] == 3) {
|
||
$extra = [];
|
||
//合成视频按时长扣费
|
||
if (in_array($scene, [
|
||
self::AUDIO_TRAINING_YM, self::VIDEO_TRAINING_YM
|
||
])) {
|
||
|
||
$duration = $response['data']['duration'] ?? 1;
|
||
$points = ceil($duration * $unit);
|
||
$extra = ['音视频时长' => $duration, '算力单价' => $unit, '实际消耗算力' => $points];
|
||
}
|
||
|
||
//token扣除
|
||
User::userTokensChange($userId, $points);
|
||
//记录日志
|
||
AccountLogLogic::recordUserTokensLog(true, $userId, $tokenCode, $points, $taskId, $extra);
|
||
}
|
||
return true;
|
||
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 保存base64文件
|
||
*/
|
||
public static function saveBase64File(string $base64String, string $type)
|
||
{
|
||
|
||
$typePath = match ($type) {
|
||
'avatar' => 'images',
|
||
'audio' => 'audio',
|
||
'video' => 'video',
|
||
};
|
||
|
||
$savePath = public_path('uploads/' . $typePath . '/' . date('Ymd'));
|
||
|
||
// 检查保存路径是否存在,不存在则创建
|
||
if (!is_dir($savePath)) {
|
||
|
||
mkdir($savePath, 0777, true);
|
||
}
|
||
|
||
// 获取 MIME 类型和 Base64 数据
|
||
if (preg_match('/^data:image\/(\w+);base64,/', $base64String, $matches)) {
|
||
$mimeType = $matches[1]; // 获取 MIME 类型(如 png、jpeg、gif 等)
|
||
$base64String = substr($base64String, strpos($base64String, ',') + 1); // 移除 Base64 前缀
|
||
} else {
|
||
return "";
|
||
}
|
||
|
||
// 检查支持的 MIME 类型并获取对应的文件后缀
|
||
$extensionMap = [
|
||
// 视频类型
|
||
'mp4' => '.mp4',
|
||
'avi' => '.avi',
|
||
'mov' => '.mov',
|
||
'wmv' => '.wmv',
|
||
'flv' => '.flv',
|
||
'mkv' => '.mkv',
|
||
// 音频类型
|
||
'mp3' => '.mp3',
|
||
'wav' => '.wav',
|
||
'aac' => '.aac',
|
||
'ogg' => '.ogg',
|
||
'flac' => '.flac',
|
||
// 图片类型
|
||
'png' => '.png',
|
||
'jpeg' => '.jpg',
|
||
'jpg' => '.jpg',
|
||
'gif' => '.gif',
|
||
'bmp' => '.bmp',
|
||
'webp' => '.webp'
|
||
];
|
||
|
||
$fileExtension = $extensionMap[$mimeType] ?? '.bin'; // 未知类型默认为 .bin
|
||
|
||
// 解码 Base64 数据
|
||
$imageData = base64_decode($base64String);
|
||
|
||
// 检查解码是否成功
|
||
if ($imageData === false) {
|
||
|
||
return "";
|
||
}
|
||
|
||
$finalFileName = generate_unique_task_id() . $fileExtension;
|
||
|
||
// 保存图片到文件
|
||
$filePath = $savePath . $finalFileName;
|
||
|
||
if (file_put_contents($filePath, $imageData)) {
|
||
|
||
return FileService::getFileUrl(str_replace(public_path(), '', $filePath));
|
||
} else {
|
||
|
||
return "";
|
||
}
|
||
}
|
||
|
||
public static function copywriting($data){
|
||
|
||
$keywords = $data['keywords'] ?? '';
|
||
$number = $data['number'] ?? '';
|
||
if (empty($keywords) || empty($number)) {
|
||
message('参数错误');
|
||
}
|
||
|
||
$taskId = generate_unique_task_id();
|
||
$request = [
|
||
'keywords' => $keywords,
|
||
'number' => $number,
|
||
];
|
||
$scene = self::COPYWRITING_CREATE;
|
||
|
||
$result = self::requestUrl($request, $scene, self::$uid, $taskId);
|
||
if (!empty($result) && isset($result['content'])) {
|
||
self::$returnData = $result;
|
||
} else {
|
||
self::setError('生成失败');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
public static function getVideoThumbnailFromUrl($videoUrl, $time = '00:00:01')
|
||
{
|
||
try {
|
||
// 生成缩略图保存路径
|
||
$dirPath = public_path() . 'uploads/images/' . date('Ymd') . '/';
|
||
$thumbnailname = date('YmdHis') . substr(md5($videoUrl), 0, 5)
|
||
. str_pad(rand(0, 9999), 4, '0', STR_PAD_LEFT) . '.jpg';
|
||
$thumbnailPath = $dirPath . $thumbnailname;
|
||
// 检查文件夹是否存在,不存在则创建
|
||
if (!file_exists($dirPath)) {
|
||
mkdir($dirPath, 0777, true); // 0777 是权限模式,true 表示递归创建
|
||
}
|
||
// 构建 FFmpeg 命令,直接使用视频的 URL
|
||
$command = "ffmpeg -i " . escapeshellarg($videoUrl) . " -ss " . escapeshellarg($time) . " -vframes 1 " . escapeshellarg($thumbnailPath) . " 2>&1";
|
||
// 执行命令
|
||
$output = shell_exec($command);
|
||
// 检查是否成功生成缩略图
|
||
if (file_exists($thumbnailPath)) {
|
||
return 'uploads/images/' . date('Ymd') . '/' . $thumbnailname;
|
||
} else {
|
||
return false;
|
||
}
|
||
} catch (\Exception $e) {
|
||
Log::write('获取图片任务失败11' . $e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
}
|