FastMovieAI AI短剧创作平台 二开基线

- 源码: xhadmincn/FastMovieAI (Apache 2.0)
- 二开: Docker部署, Swoole改Select, route.php修复, 补update/VERSION
- vendor/示例资源已gitignore
This commit is contained in:
李建琦
2026-08-26 18:37:36 +08:00
commit eae151fd57
888 changed files with 105571 additions and 0 deletions
@@ -0,0 +1,43 @@
<?php
namespace plugin\model\app\api\controller;
use app\Basic;
use app\expose\enum\State;
use plugin\model\app\model\PluginModel;
use plugin\model\utils\enum\ModelPointType;
use plugin\shortplay\utils\PricingCalculator;
class ModelController extends Basic
{
protected $notNeedLoginAll = ['models'];
public function models()
{
$PluginModel = PluginModel::where(['state' => State::YES['value']])->order('sort asc')->field('id,channels_uid,icon,name,point,point_type,scene,description,form')->select()->each(function ($item) {
$item->unit_point = $item->point;
if ($item->point_type == ModelPointType::FIELD_RULE['value']) {
$formData = [
'resolution' => '720P',
'duration' => 5,
];
$calculator = new PricingCalculator($item->form, $formData, $item->point);
$item->point = $calculator->calculate() / 5;
}
});
$models = [];
foreach ($PluginModel as $model) {
$models[$model->scene][] = [
'id' => $model->id,
'icon' => $model->icon,
'name' => $model->name,
'point' => $model->point,
'scene' => $model->scene,
'description' => $model->description,
'form' => $model->form,
'unit_point' => $model->unit_point,
'unit' => ModelPointType::get($model->point_type, 'unit')
];
}
return $this->resData($models);
}
}
@@ -0,0 +1,44 @@
<?php
namespace plugin\model\app\api\controller;
use app\Basic;
use plugin\model\app\model\PluginModelTask;
use plugin\model\utils\enum\ModelTaskStatus;
use support\Request;
class TaskController extends Basic
{
public function index(Request $request)
{
$limit = $request->get('limit', 10);
$where = [];
$where[] = ['uid', '=', $request->uid];
$where[] = ['status', 'in', [ModelTaskStatus::WAIT['value'], ModelTaskStatus::PROCESSING['value'], ModelTaskStatus::WAIT_DOWNLOAD['value'], ModelTaskStatus::DOWNLOADING['value'], ModelTaskStatus::UPLOADING['value'], ModelTaskStatus::SUCCESS['value']]];
$scene = $request->get('scene', 'all');
if ($scene != 'all') {
$where[] = ['scene', '=', $scene];
}
$scenes=$request->get('scenes');
if ($scenes) {
$where[] = ['scene', 'in', $scenes];
}
$model_type = $request->get('model_type', 'all');
if ($model_type != 'all') {
$where[] = ['model_type', '=', $model_type];
}
$alias_id = $request->get('alias_id');
if ($alias_id) {
$where[] = ['alias_id', '=', $alias_id];
}
$list = PluginModelTask::where($where)
->with(['user' => function ($query) {
$query->field('id,nickname,headimg,mobile,channels_uid');
}, 'result' => function ($query) {
$query->field('task_id,result,image,image_path,video_path,message,channels_uid');
}])
->order('id desc')
->paginate($limit);
return $this->resData($list);
}
}
@@ -0,0 +1,166 @@
<?php
namespace plugin\model\app\api\controller;
use app\Basic;
use app\expose\enum\State;
use FFMpeg\FFMpeg;
use FFMpeg\Format\Audio\Mp3;
use FFMpeg\Format\Audio\Wav;
use plugin\control\expose\helper\Uploads;
use plugin\control\utils\yidevs\Yidevs;
use plugin\model\app\model\PluginModel;
use plugin\model\app\model\PluginModelVoice;
use plugin\model\app\model\PluginModelVoiceText;
use plugin\model\utils\enum\ModelScene;
use plugin\model\utils\enum\ModelVoiceStatus;
use plugin\notification\expose\helper\Push;
use support\Request;
use Webman\Http\UploadFile;
use Workerman\Coroutine;
class VoiceController extends Basic
{
public function submit(Request $request)
{
$D = $request->post();
if (empty($D['text_id'])) {
return $this->fail('请选择文案');
}
if (empty($D['model_id'])) {
return $this->fail('请选择模型');
}
$PluginModelVoiceText = PluginModelVoiceText::where(['id' => $D['text_id'], 'channels_uid' => $request->channels_uid])->find();
if (!$PluginModelVoiceText) {
return $this->fail('文案不存在');
}
$PluginModel = PluginModel::where(['id' => $D['model_id'], 'channels_uid' => $request->channels_uid, 'state' => State::YES['value'], 'scene' => ModelScene::DIALOGUE_VOICE['value']])->find();
if (!$PluginModel) {
return $this->fail('模型不存在');
}
$audio_url = '';
$file = $request->file('file');
if (empty($file)) {
return $this->fail('请录制语音');
}
$fileName = uniqid('voice_') . '.wav';
$temp_path = runtime_path('temp/' . $fileName);
// 判断文件的mime是否为MP3或WAV
if (!str_starts_with($file->getUploadMimeType(), 'audio/wav')) {
// 使用phpffmpeg转换为MP3
$ffmpeg = \FFMpeg\FFMpeg::create([
'ffmpeg.binaries' => '/www/server/ffmpeg/ffmpeg-6.1/ffmpeg',
'ffprobe.binaries' => '/www/server/ffmpeg/ffmpeg-6.1/ffprobe',
]);
$media = $ffmpeg->open($file->getRealPath());
$wav = new Wav();
$media->save($wav, $temp_path);
$file = new UploadFile($temp_path, $fileName, 'audio/wav', $file->getUploadErrorCode());
}
if ($file->getSize() > 1024 * 1024 * 10) {
unlink($temp_path);
return $this->fail('语音文件大小不能超过10MB,请重新录制');
}
$result = Uploads::upload($request->channels_uid, $file, null, 'uploads/voice_audio');
if (file_exists($temp_path)) {
unlink($temp_path);
}
$audio_url = $result['url'];
$PluginModelVoice = new PluginModelVoice();
$PluginModelVoice->uid = $request->uid;
$PluginModelVoice->channels_uid = $request->channels_uid;
if (!empty($D['voice_id'])) {
$PluginModelVoice = PluginModelVoice::where(['id' => $D['voice_id'], 'channels_uid' => $request->channels_uid, 'uid' => $request->uid])->find();
if (!$PluginModelVoice) {
return $this->fail('音色不存在');
}
$audio_url = $PluginModelVoice->audio_url;
}
$PluginModelVoice->text_id = $D['text_id'];
$PluginModelVoice->model_id = $D['model_id'];
if ($audio_url) {
$PluginModelVoice->audio = $audio_url;
}
$PluginModelVoice->status = ModelVoiceStatus::WAIT['value'];
$PluginModelVoice->save();
$ids = [
'id' => $PluginModelVoice->id,
'uid' => $request->uid,
'channels_uid' => $request->channels_uid,
];
$data = [
'model' => $PluginModel->model_id,
'notify_url' => 'https://' . $request->host() . '/app/model/Notify/voiceClone',
'form_data' => [
'audio' => $audio_url,
]
];
Coroutine::create(function () use ($ids, $data) {
$status = ModelVoiceStatus::CLONING['value'];
try {
$result = Yidevs::AudioVoiceClone($ids['channels_uid'], $data);
$PluginModelVoice = PluginModelVoice::where(['id' => $ids['id'], 'channels_uid' => $ids['channels_uid']])->find();
$PluginModelVoice->status = ModelVoiceStatus::CLONING['value'];
$PluginModelVoice->task_id = $result['task_id'];
$PluginModelVoice->save();
} catch (\Throwable $th) {
$PluginModelVoice = PluginModelVoice::where(['id' => $ids['id'], 'channels_uid' => $ids['channels_uid']])->find();
$PluginModelVoice->status = ModelVoiceStatus::FAIL['value'];
$PluginModelVoice->message = $th->getMessage();
$PluginModelVoice->save();
$status = ModelVoiceStatus::FAIL['value'];
}
Push::send([
'uid' => $ids['uid'],
'event' => 'clonevoice',
], [
'id' => $PluginModelVoice->id,
'status' => $status,
]);
});
return $this->resData(['voice_id' => $PluginModelVoice->id]);
}
public function update(Request $request)
{
$id = $request->post('id');
$PluginModelVoice = PluginModelVoice::where(['id' => $id, 'channels_uid' => $request->channels_uid, 'uid' => $request->uid])->find();
if (!$PluginModelVoice) {
return $this->fail('音色不存在');
}
$name = $request->post('name');
if ($name) {
$PluginModelVoice->name = $name;
}
$description = $request->post('description');
if ($description) {
$PluginModelVoice->description = $description;
}
$image = $request->post('image');
if ($image) {
$PluginModelVoice->image = $image;
}
$headimg = $request->post('headimg');
if ($headimg) {
$PluginModelVoice->headimg = $headimg;
}
$gender = $request->post('gender');
if ($gender) {
$PluginModelVoice->gender = $gender;
}
$age = $request->post('age');
if ($age) {
$PluginModelVoice->age = $age;
}
$PluginModelVoice->save();
return $this->success('更新成功');
}
public function detail(Request $request)
{
$id = $request->get('id');
$PluginModelVoice = PluginModelVoice::where(['id' => $id, 'channels_uid' => $request->channels_uid, 'uid' => $request->uid])->find();
if (!$PluginModelVoice) {
return $this->fail('音色不存在');
}
return $this->resData($PluginModelVoice);
}
}
@@ -0,0 +1,24 @@
<?php
namespace plugin\model\app\api\controller;
use app\Basic;
use app\expose\enum\State;
use plugin\model\app\model\PluginModelVoiceText;
use support\Request;
class VoiceTextController extends Basic
{
public function index(Request $request)
{
$where = [];
$where[] = ['channels_uid', '=', $request->channels_uid];
$where[] = ['state', '=', State::YES['value']];
$text = $request->get('text');
if ($text) {
$where[] = ['text', 'like', "%{$text}%"];
}
$list = PluginModelVoiceText::where($where)->order('id desc')->select();
return $this->resData($list);
}
}