xiaoai/php_server/app/api/logic/sv/SvVideoTaskLogic.php

620 lines
22 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\api\logic\sv;
use app\api\logic\service\TokenLogService;
use app\common\model\sv\SvVideoSetting;
use app\common\model\sv\SvVideoTask;
use think\facade\Db;
use think\facade\Log;
use app\common\enum\user\AccountLogEnum;
use app\common\logic\AccountLogLogic;
use app\common\model\user\User;
use app\common\model\user\UserTokensLog;
use app\common\service\FileService;
use TencentCloud\Teo\V20220106\Models\Sv;
/**
* SvVideoTaskLogic
* @desc 视频设置逻辑处理
*/
class SvVideoTaskLogic extends SvBaseLogic
{
const AUDIO_TRAINING_YM = 'audioTrainingYm'; //文字转音频
const VIDEO_TRAINING_YM = 'videoTrainingYm'; //视频训练
const AUDIO_TRAINING_YMT = 'audioTrainingYmt'; //文字转音频
const VIDEO_TRAINING_YMT = 'videoTrainingYmt'; //视频训练
/**
* 处理音频合成任务
* @param string $taskId 任务ID
* @return void
*/
public static function compositeAudioCron(string $taskId = '')
{
try {
// 构建查询条件:待处理状态的任务
$where = [
['status', '=', 0], // 待处理
['model_version', '=', 4],
['tries', '<', 5]
];
if (!empty($taskId)) {
$where[] = ['task_id', '=', $taskId];
}
// 获取待处理的任务限制5条
$tasks = SvVideoTask::where($where)
->order('tries DESC, id ASC')
->limit(5)
->select();
if ($tasks->isEmpty()) {
// Log::channel('sv')->info('没有需要处理的音频任务');
return;
}
foreach ($tasks as $task) {
try {
// Log::channel('sv')->info('开始处理音频任务'. json_encode(['task_id' => $task->task_id]));
SvVideoSetting::where('id', $task->video_setting_id)->where('status','1')->update(['status' => 1]);
// 更新状态为音频合成中
switch ($task->model_version) {
case 4:
$scene = self::AUDIO_TRAINING_YM;
break;
case 6:
$scene = self::AUDIO_TRAINING_YMT;
break;
default:
$scene = self::AUDIO_TRAINING_YM;
break;
}
// 请求合成音频
$response = self::requestUrl([
'msg' => $task->msg,
'voice_id' => $task->voice_id,
'language' => 0,
'notify_url' => '/api/sv.videoTask/notify'
], $scene, $task->user_id, $task->task_id);
if (!isset($response['id']) || empty($response['id'])) {
$task->tries = $task->tries +1;
if ( $task->tries == 5){
$task->remark = '音频合成5次失败';;
$task->status = 2;
}
$task->save();
//Log::channel ('sv')->info('音频合成失败'. json_encode($response));
return ;
}
$task->tries = 0;
// 更新音频信息
$task->audio_id = $response['id'];
$task->status = 1; // 更新状态为音频合成成功
if (!$task->save()) {
throw new \Exception("更新音频结果失败");
}
} catch (\Exception $e) {
$task->tries = $task->tries +1;
// Log::channel('sv')->info('音频任务处理失败'. $task->task_id. $e->getMessage());
$task->remark = $e->getMessage();
$task->save();
}
}
} catch (\Exception $e) {
Log::channel('sv')->info('批量处理音频任务失败'. $e->getMessage());
}
}
/**
* 处理视频合成任务
* @param string $taskId 任务ID
* @return void
*/
public static function compositeVideoCron(string $taskId = '')
{
try {
// 构建查询条件:音频合成成功的任务
$where = [
['status', '=', 3], // 音频合成成功
['model_version', '=', 4],
['tries', '<', 5]
];
if (!empty($taskId)) {
$where[] = ['task_id', '=', $taskId];
}
// 获取待处理的任务限制5条
$tasks = SvVideoTask::where($where)
->order('speed DESC, id ASC')
->limit(5)
->select();
if ($tasks->isEmpty()) {
//Log::channel('sv')->info('没有需要处理的视频任务');
return;
}
foreach ($tasks as $task) {
try {
// Log::channel('sv')->info('开始处理视频任务'.'task_id'. $task->task_id);
// 更新状态为视频合成中
switch ($task->model_version) {
case 4:
$scene = self::VIDEO_TRAINING_YM;
break;
case 6:
$scene = self::VIDEO_TRAINING_YMT;
break;
default:
$scene = self::VIDEO_TRAINING_YM;
break;
}
$response = self::requestUrl([
'name' => $task->name,
'avatar_id' => $task->anchor_id,
'video_url' => $task->upload_video_url,
'audio_url' => $task->audio_url,
'priority' => $task->speed,
'notify_url' => '/api/sv.videoTask/notify'
], $scene, $task->user_id, $task->task_id);
if (!isset($response['id']) || empty($response['id'])) {
$task->tries = $task->tries + 1;
if ( $task->tries == 5){
$task->status = 5;
$task->remark = '视频合成5次失败';;
}
$task->save();
return;
}
// 更新视频结果
$task->result_id = $response['id'];
$task->status = 4;
if (!$task->save()) {
throw new \Exception("更新视频结果失败");
}
// Log::channel('sv')->info('视频合成成功'. 'task_id' .$task->task_id.$task->result_id);
} catch (\Exception $e) {
// Log::channel('sv')->info('视频任务处理失败'.'task_id'.$task->task_id.$e->getMessage());
$task->tries = $task->tries + 1;
$task->remark = $e->getMessage();
$task->save();
}
}
} catch (\Exception $e) {
Log::channel('sv')->info('批量处理视频任务失败' . $e->getMessage());
}
}
/**
* 查询音频合成结果任务
* @param string $taskId 任务ID
* @return void
*/
public static function queryAudioCron(string $taskId = '')
{
try {
// 构建查询条件:音频合成成功的任务
$where = [
['status', '=', 1], // 音频已合成
['model_version', '=', 4],
['tries', '<', 20]
];
if (!empty($taskId)) {
$where[] = ['task_id', '=', $taskId];
}
// 获取待处理的任务限制5条
$tasks = SvVideoTask::where($where)
->order('tries DESC, id ASC')
->limit(2)
->select()->toArray();
if (!$tasks) {
//Log::channel('sv')->info('没有需要查询音频任务');
return;
}
$methodMap = [
4 => 'detailYm',
6 => 'detailYmt',
];
$typeMap = [
4 => AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YM,
6 => AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YMT,
];
foreach ($tasks as $task) {
try {
// Log::channel('sv')->info('开始查询音频任务' . $task->task_id);
unset($task['create_time']);
$task['update_time'] = time();
$method = $methodMap[$task['model_version']] ?? 'detailYmt';
$response = \app\common\service\ToolsService::Human()->$method([
'type' => 3,
'id' => $task['audio_id']
]);
if (isset($response['data']['status']) && $response['data']['status'] == 3) {
$upload_url = FileService::downloadFileBySource($response['data']['speech_url'], 'audio');
$task['tries'] = 0;
$task['status'] = 3;
$task['audio_result_url'] = $response['data']['speech_url'];
$task['audio_url'] = $upload_url;
}elseif(isset($response['data']['status']) && $response['data']['status'] == 4) {
$typeID = $typeMap[$task['model_version']] ?? AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YMT;
//查询是否已返还
if (UserTokensLog::where('user_id', $task['user_id'])->where('change_type', $typeID)->where('action', 1)->where('task_id', $task['task_id'])->count() == 0) {
$points = UserTokensLog::where('user_id', $task['user_id'])->where('change_type', $typeID)->where('task_id', $task['task_id'])->value('change_amount') ?? 0;
AccountLogLogic::recordUserTokensLog(false, $task['user_id'], $typeID, $points, $task['task_id']);
}
$task['status'] = 2;
}
$task['tries'] = $task['tries'] + 1;
SvVideoTask::update($task, ['id' => $task['id']]);
} catch (\Exception $e) {
$task['tries'] = $task['tries'] + 1;
// Log::channel('sv')->info('视频任务处理失败'. $task['task_id'] .$e->getMessage());
$task['remark'] = $e->getMessage();
SvVideoTask::update($task, ['id' => $task['id']]);
}
}
} catch (\Exception $e) {
Log::channel('sv')->info('批量处理视频任务失败'. $e->getMessage());
}
}
/**
* 请求上游接口与计费
* @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_YM => ['human_audio_ym', AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_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::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::AUDIO_TRAINING_YM:
$response = $requestService->audioTrainingYm($request);
break;
case self::VIDEO_TRAINING_YM:
$response = $requestService->videoTrainingYm($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_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 $data
* @param string $modelVersion
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function updateAudio(array $data, string $modelVersion): bool
{
$model = SvVideoTask::where('model_version', $modelVersion)->where('status', 1);
if (in_array($modelVersion,[4,6])) {
$model = $model->where('audio_id', $data['audio_id']);
} elseif ($modelVersion == 2) {
// 目前是同步的 没有回调
return true;
} else{
return false;
}
$model->select()
->each(function ($item) use ($data) {
if (in_array($item->model_version,[4,6])) {
$item->status = ($data['url'] != "") ? 1 : 2;
$item->url = FileService::downloadFileBySource($data['url'], 'audio');
$scene = $item->model_version == 4 ? "human_audio_ym" : "human_audio_ymt";
// TODO 失败退费
if ($item->status == 2) {
self::refundTokens($item->user_id, $item->anchor_id, $item->task_id, $scene);
}
// 更新视频
SvVideoTask::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 = SvVideoTask::where('model_version', $modelVersion)->where('status', 4);
if (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 (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) ? 6 : 5;
$scene = $item->model_version == 4 ? "human_video_ym" : "human_video_ymt";
$videoSetting = SvVideoSetting::where('id', $item->video_setting_id)->find();
if($item->status == 5){
self::refundTokens($item->user_id, $item->result_id, $item->task_id, $scene);
$videoSetting->error_num += 1;
}else{
$videoSetting->success_num += 1;
}
$num = $videoSetting->video_count - $videoSetting->success_num;
if ( $videoSetting->error_num == $num){
$videoSetting->status = 5;
}
if ($videoSetting->success_num == $videoSetting->video_count){
$videoSetting->status = 3;
}
if ($videoSetting->error_num == $videoSetting->video_count){
$videoSetting->status = 4;
}
$videoSetting->save();
} else {
$item->status = 4;
}
$item->video_result_url = FileService::downloadFileBySource($data['video_Url'], 'video');
$item->remark = $data['message'] ?? '';
}
$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_audio_ym' => [3, AccountLogEnum::TOKENS_DEC_HUMAN_AUDIO_YM],
'human_video_ym' => [4, AccountLogEnum::TOKENS_DEC_HUMAN_VIDEO_YM],
'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);
} 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;
}
}
public static function updateSvVideoTask(array $params)
{
try {
$task = SvVideoTask::where('id',$params['id'])->where('user_id', self::$uid)
->find();
if (!$task) {
self::setError('视频不存在');
return false;
}
unset($params['status'],$params['user_id']);
$task->update($params);
self::$returnData = $task->refresh()->toArray();
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
public static function deleteSvVideoTask(int $id)
{
try {
$task = SvVideoTask::where('id',$id)->whereIn('status',[3,5,6])->where('user_id', self::$uid)
->find();
if (!$task) {
self::setError('视频不存在');
return false;
};
$task->delete();
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
public static function retrySvVideoTask(array $params){
Db::startTrans();
try {
$task = SvVideoTask::where('id', $params['id'])->whereIn('status',[2,5])->where('user_id', self::$uid)
->find();
if (!$task) {
self::setError('视频不存在');
return false;
};
$setting = SvVideoSetting::where('id', $task->video_setting_id)->field('id,error_num,status')->find();
if (!$setting) {
self::setError('任务不存在');
return false;
};
if ($task['status'] == 2){
$update['status'] = 0;
}else{
$update['status'] = 3;
}
$update['tries'] = 0;
$update['id'] = $params['id'];
$task->update($update);
$set['error_num'] = $setting['error_num'] -1;
$set['status'] = 2;
$set['id'] = $setting['id'];
$setting->update($set);
Db::commit();
return true;
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
}
}