Files
fastmovieai/fastmovie-admin/plugin/shortplay/app/api/controller/StoryboardController.php
T
李建琦 eae151fd57 FastMovieAI AI短剧创作平台 二开基线
- 源码: xhadmincn/FastMovieAI (Apache 2.0)
- 二开: Docker部署, Swoole改Select, route.php修复, 补update/VERSION
- vendor/示例资源已gitignore
2026-08-26 18:37:36 +08:00

781 lines
38 KiB
PHP

<?php
namespace plugin\shortplay\app\api\controller;
use app\Basic;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
use plugin\control\expose\helper\Uploads;
use plugin\model\app\model\PluginModelTask;
use plugin\model\utils\enum\ModelScene;
use plugin\model\utils\enum\ModelTaskStatus;
use plugin\notification\expose\helper\Push;
use plugin\shortplay\app\model\PluginShortplayActor;
use plugin\shortplay\app\model\PluginShortplayActorCharacterLook;
use plugin\shortplay\app\model\PluginShortplayDrama;
use plugin\shortplay\app\model\PluginShortplayDramaActor;
use plugin\shortplay\app\model\PluginShortplayDramaEpisode;
use plugin\shortplay\app\model\PluginShortplayDramaEpisodeActor;
use plugin\shortplay\app\model\PluginShortplayDramaStoryboard;
use plugin\shortplay\app\model\PluginShortplayDramaStoryboardActor;
use plugin\shortplay\app\model\PluginShortplayDramaStoryboardDialogue;
use plugin\shortplay\app\model\PluginShortplayDramaStoryboardProp;
use plugin\shortplay\app\model\PluginShortplayProp;
use plugin\shortplay\utils\enum\ActorStatus;
use plugin\shortplay\utils\enum\PropStatus;
use support\Cache;
use support\Log;
use support\Request;
use support\think\Db;
use Workerman\Coroutine;
class StoryboardController extends Basic
{
public function index(Request $request)
{
$type = $request->get('type');
$where = [];
switch ($type) {
case 'episode':
$episode_id = $request->get('episode_id');
if (!$episode_id) {
return $this->fail('分集ID不能为空');
}
$PluginShortplayDramaEpisode = PluginShortplayDramaEpisode::where(['id' => $episode_id])->find();
if (!$PluginShortplayDramaEpisode) {
return $this->fail('分集不存在');
}
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $PluginShortplayDramaEpisode->drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$where[] = ['episode_id', '=', $PluginShortplayDramaEpisode->id];
break;
case 'drama':
break;
default:
$drama_id = $request->get('drama_id');
if (!$drama_id) {
return $this->fail('短剧ID不能为空');
}
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$where[] = ['drama_id', '=', $PluginShortplayDrama->id];
break;
}
$description = $request->get('description');
if ($description) {
$where[] = ['description', 'like', '%' . $description . '%'];
}
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where($where)->with(['sceneFind', 'props', 'dialogues'])->order('sort asc,id asc')->select()->each(function ($item) {});
return $this->resData($PluginShortplayDramaStoryboard);
}
public function updateSort(Request $request)
{
$drama_id = $request->post('drama_id');
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$storyboards = $request->post('storyboards');
if (empty($storyboards)) {
$id = $request->post('id');
$sort = $request->post('sort');
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['drama_id' => $PluginShortplayDrama->id, 'id' => $id])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
$PluginShortplayDramaStoryboard->sort = $sort;
$PluginShortplayDramaStoryboard->save();
} else {
Db::startTrans();
try {
foreach ($storyboards as $storyboard) {
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['drama_id' => $PluginShortplayDrama->id, 'id' => $storyboard['id']])->find();
if (!$PluginShortplayDramaStoryboard) {
continue;
}
$PluginShortplayDramaStoryboard->sort = $storyboard['sort'];
$PluginShortplayDramaStoryboard->save();
}
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return $this->fail($th->getMessage());
}
}
return $this->success('更新成功');
}
public function copyStoryboard(Request $request)
{
$drama_id = $request->post('drama_id');
$copy_id = $request->post('copy_id'); // 要复制的 storyboard ID
$new_sort = $request->post('new_sort');
// 校验短剧
$PluginShortplayDrama = PluginShortplayDrama::where([
'id' => $drama_id,
'uid' => $request->uid
])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
// 校验原分镜
$origin = PluginShortplayDramaStoryboard::where([
'drama_id' => $drama_id,
'id' => $copy_id
])->find();
if (!$origin) {
return $this->fail('原分镜不存在');
}
// 校验 new_sort
if (!$new_sort || !is_numeric($new_sort)) {
return $this->fail('new_sort 不合法');
}
Db::startTrans();
try {
// 1. 查询当前 scene 内所有分镜
$sceneStoryboards = PluginShortplayDramaStoryboard::where([
'drama_id' => $drama_id,
'episode_id' => $origin->episode_id,
])
->order('sort asc')
->select()
->toArray();
// 2. 将 sort >= new_sort 的分镜全部 +1
foreach ($sceneStoryboards as $sb) {
if ($sb['sort'] >= $new_sort) {
PluginShortplayDramaStoryboard::where(['id' => $sb['id']])
->update(['sort' => $sb['sort'] + 1]);
}
}
// 3. 插入复制后的分镜
unset($origin['id']); // 删除原分镜ID
$origin['sort'] = $new_sort;
$origin['create_time'] = time();
$origin['update_time'] = time();
$PluginShortplayDramaStoryboard = new PluginShortplayDramaStoryboard;
$PluginShortplayDramaStoryboard->save($origin);
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return $this->fail($th->getMessage());
}
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $PluginShortplayDramaStoryboard->id])->with(['actors', 'sceneFind', 'props', 'dialogues'])->find();
return $this->resData($PluginShortplayDramaStoryboard);
}
public function deleteStoryboard(Request $request)
{
$drama_id = $request->post('drama_id');
$episode_id = $request->post('episode_id');
$id = $request->post('id');
// 校验短剧是否存在
$PluginShortplayDrama = PluginShortplayDrama::where([
'id' => $drama_id,
'uid' => $request->uid
])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$PluginShortplayDramaEpisode = PluginShortplayDramaEpisode::where([
'id' => $episode_id,
'drama_id' => $drama_id
])->find();
if (!$PluginShortplayDramaEpisode) {
return $this->fail('分集不存在');
}
if ($id) {
// 校验分镜是否存在
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where([
'id' => $id,
'drama_id' => $drama_id,
])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
}
Db::startTrans();
try {
if ($id) {
PluginShortplayDramaStoryboardActor::where(['storyboard_id' => $PluginShortplayDramaStoryboard->id])->delete();
PluginShortplayDramaStoryboardDialogue::where(['storyboard_id' => $PluginShortplayDramaStoryboard->id])->delete();
$PluginShortplayDramaStoryboardProps = PluginShortplayDramaStoryboardProp::where(['storyboard_id' => $PluginShortplayDramaStoryboard->id])->select();
foreach ($PluginShortplayDramaStoryboardProps as $PluginShortplayDramaStoryboardProp) {
PluginShortplayProp::where(['id' => $PluginShortplayDramaStoryboardProp->prop_id, 'status' => PropStatus::INITIALIZING['value']])->delete();
$PluginShortplayDramaStoryboardProp->delete();
}
$PluginShortplayDramaStoryboard->delete();
$PluginShortplayDramaStoryboards = PluginShortplayDramaStoryboard::where([
'drama_id' => $drama_id,
'episode_id' => $episode_id,
])
->order('sort asc')
->select();
$sort = 1;
foreach ($PluginShortplayDramaStoryboards as $sb) {
$sb->sort = $sort++;
$sb->save();
}
} else {
$PluginShortplayDramaStoryboards = PluginShortplayDramaStoryboard::where([
'drama_id' => $drama_id,
'episode_id' => $episode_id,
])->select();
foreach ($PluginShortplayDramaStoryboards as $PluginShortplayDramaStoryboard) {
PluginShortplayDramaStoryboardActor::where(['storyboard_id' => $PluginShortplayDramaStoryboard->id])->delete();
PluginShortplayDramaStoryboardDialogue::where(['storyboard_id' => $PluginShortplayDramaStoryboard->id])->delete();
PluginShortplayDramaStoryboardProp::where(['storyboard_id' => $PluginShortplayDramaStoryboard->id])->delete();
$PluginShortplayDramaStoryboard->delete();
}
PluginShortplayProp::where(['episode_id' => $PluginShortplayDramaEpisode->id, 'status' => PropStatus::INITIALIZING['value']])->delete();
}
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return $this->fail($th->getMessage());
}
return $this->success('删除成功');
}
public function insertAfter(Request $request)
{
$drama_id = $request->post('drama_id');
$episode_id = $request->post('episode_id');
$after_id = $request->post('after_id');
$drama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$drama) {
return $this->fail('短剧不存在');
}
$afterSort = 0;
if ($after_id) {
$after = PluginShortplayDramaStoryboard::where([
'drama_id' => $drama->id,
'id' => $after_id,
])->find();
if (!$after) {
return $this->fail('指定的分镜不存在');
}
$afterSort = $after->sort;
}
Db::startTrans();
try {
// 从 after.sort + 1 开始,所有后续 +1
PluginShortplayDramaStoryboard::where([
'drama_id' => $drama_id,
'episode_id' => $episode_id,
])
->where('sort', '>', $afterSort)
->inc('sort', 1)
->update();
// 插入新的空白分镜
$new = new PluginShortplayDramaStoryboard();
$new->channels_uid = $request->channels_uid;
$new->drama_id = $drama->id;
$new->episode_id = $episode_id;
$new->sort = $afterSort + 1;
$new->duration = 5000;
$new->save();
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return $this->fail($th->getMessage());
}
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $new->id])->with(['actors', 'sceneFind', 'props', 'dialogues'])->find();
return $this->resData($PluginShortplayDramaStoryboard);
}
public function update(Request $request)
{
$drama_id = $request->post('drama_id');
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$episode_id = $request->post('episode_id');
$id = $request->post('id');
$D = $request->post();
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $id, 'drama_id' => $drama_id, 'episode_id' => $episode_id])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
if (!empty($D['scene_id'])) {
$PluginShortplayDramaStoryboard->scene_id = $D['scene_id'];
}
if (!empty($D['description'])) {
$PluginShortplayDramaStoryboard->description = $D['description'];
}
if (!empty($D['shot_type'])) {
$PluginShortplayDramaStoryboard->shot_type = $D['shot_type'];
}
if (!empty($D['shot_angle'])) {
$PluginShortplayDramaStoryboard->shot_angle = $D['shot_angle'];
}
if (!empty($D['shot_motion'])) {
$PluginShortplayDramaStoryboard->shot_motion = $D['shot_motion'];
}
if (!empty($D['image'])) {
$PluginShortplayDramaStoryboard->image = $D['image'];
$PluginShortplayDramaStoryboard->use_material_type = 'image';
}
if (!empty($D['video'])) {
$PluginShortplayDramaStoryboard->video = $D['video'];
$PluginShortplayDramaStoryboard->use_material_type = 'video';
}
if (isset($D['narration'])) {
$PluginShortplayDramaStoryboard->narration = $D['narration'];
}
if (isset($D['sfx'])) {
$PluginShortplayDramaStoryboard->sfx = $D['sfx'];
}
$PluginShortplayDramaStoryboard->save();
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $PluginShortplayDramaStoryboard->id])->with(['actors', 'sceneFind', 'props', 'dialogues'])->find();
return $this->resData($PluginShortplayDramaStoryboard);
}
public function joinActor(Request $request)
{
$drama_id = $request->post('drama_id');
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$storyboard_id = $request->post('storyboard_id');
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id, 'drama_id' => $drama_id])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
$actor_id = $request->post('actor_id');
$PluginShortplayActor = PluginShortplayActor::where(['id' => $actor_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayActor) {
$PluginShortplayActor = PluginShortplayActor::where(['id' => $actor_id, 'channels_uid' => $request->channels_uid])->whereNull('uid')->find();
}
if (!$PluginShortplayActor) {
return $this->fail('演员不存在');
}
Db::startTrans();
try {
if (!PluginShortplayDramaStoryboardActor::where(['storyboard_id' => $storyboard_id, 'actor_id' => $actor_id])->count()) {
$PluginShortplayDramaStoryboardActor = new PluginShortplayDramaStoryboardActor();
$PluginShortplayDramaStoryboardActor->channels_uid = $request->channels_uid;
$PluginShortplayDramaStoryboardActor->drama_id = $PluginShortplayDramaStoryboard->drama_id;
$PluginShortplayDramaStoryboardActor->episode_id = $PluginShortplayDramaStoryboard->episode_id;
$PluginShortplayDramaStoryboardActor->storyboard_id = $storyboard_id;
$PluginShortplayDramaStoryboardActor->actor_id = $actor_id;
$PluginShortplayDramaStoryboardActor->save();
}
if (!PluginShortplayDramaEpisodeActor::where(['episode_id' => $PluginShortplayDramaStoryboard->episode_id, 'actor_id' => $actor_id])->count()) {
$PluginShortplayDramaEpisodeActor = new PluginShortplayDramaEpisodeActor();
$PluginShortplayDramaEpisodeActor->channels_uid = $request->channels_uid;
$PluginShortplayDramaEpisodeActor->drama_id = $PluginShortplayDramaStoryboard->drama_id;
$PluginShortplayDramaEpisodeActor->episode_id = $PluginShortplayDramaStoryboard->episode_id;
$PluginShortplayDramaEpisodeActor->actor_id = $actor_id;
$PluginShortplayDramaEpisodeActor->save();
}
if (!PluginShortplayDramaActor::where(['drama_id' => $drama_id, 'actor_id' => $actor_id])->count()) {
$PluginShortplayDramaActor = new PluginShortplayDramaActor();
$PluginShortplayDramaActor->channels_uid = $request->channels_uid;
$PluginShortplayDramaActor->drama_id = $drama_id;
$PluginShortplayDramaActor->actor_id = $actor_id;
$PluginShortplayDramaActor->save();
}
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return $this->fail($th->getMessage());
}
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id])->with(['actors', 'sceneFind', 'props', 'dialogues'])->find();
return $this->resData($PluginShortplayDramaStoryboard);
}
public function deleteActor(Request $request)
{
$drama_id = $request->post('drama_id');
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$id = $request->post('id');
$storyboard_id = $request->post('storyboard_id');
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id, 'drama_id' => $drama_id])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
Db::startTrans();
try {
PluginShortplayDramaStoryboardActor::where(['id' => $id, 'storyboard_id' => $PluginShortplayDramaStoryboard->id])->delete();
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return $this->fail($th->getMessage());
}
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id])->with(['actors', 'sceneFind', 'props', 'dialogues'])->find();
return $this->resData($PluginShortplayDramaStoryboard);
}
public function joinProp(Request $request)
{
$drama_id = $request->post('drama_id');
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$storyboard_id = $request->post('storyboard_id');
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id, 'drama_id' => $drama_id])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
$prop_id = $request->post('prop_id');
$PluginShortplayProp = PluginShortplayProp::where(['id' => $prop_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayProp) {
return $this->fail('物品不存在');
}
Db::startTrans();
try {
if (!PluginShortplayDramaStoryboardProp::where(['storyboard_id' => $storyboard_id, 'prop_id' => $PluginShortplayProp->id])->count()) {
$PluginShortplayDramaStoryboardProp = new PluginShortplayDramaStoryboardProp();
$PluginShortplayDramaStoryboardProp->channels_uid = $request->channels_uid;
$PluginShortplayDramaStoryboardProp->storyboard_id = $storyboard_id;
$PluginShortplayDramaStoryboardProp->prop_id = $PluginShortplayProp->id;
$PluginShortplayDramaStoryboardProp->save();
}
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return $this->fail($th->getMessage());
}
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id])->with(['actors', 'sceneFind', 'props', 'dialogues'])->find();
return $this->resData($PluginShortplayDramaStoryboard);
}
public function deleteProp(Request $request)
{
$drama_id = $request->post('drama_id');
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$id = $request->post('id');
$storyboard_id = $request->post('storyboard_id');
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id, 'drama_id' => $drama_id])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
Db::startTrans();
try {
PluginShortplayDramaStoryboardProp::where(['id' => $id, 'storyboard_id' => $PluginShortplayDramaStoryboard->id])->delete();
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return $this->fail($th->getMessage());
}
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id])->with(['actors', 'sceneFind', 'props', 'dialogues'])->find();
return $this->resData($PluginShortplayDramaStoryboard);
}
public function ReplaceStoryboard(Request $request)
{
$id = $request->post('id');
$task_id = $request->post('task_id');
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $id])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $PluginShortplayDramaStoryboard->drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$PluginModelTask = PluginModelTask::where(['id' => $task_id, 'alias_id' => $PluginShortplayDramaStoryboard->id, 'status' => ModelTaskStatus::SUCCESS['value']])->with(['result'])->find();
if (!$PluginModelTask) {
return $this->fail('任务不存在');
}
if ($PluginModelTask->scene == ModelScene::STORYBOARD_IMAGE['value']) {
$PluginShortplayDramaStoryboard->image = $PluginModelTask->result->image_path;
$PluginShortplayDramaStoryboard->use_material_type = 'image';
}
if ($PluginModelTask->scene == ModelScene::STORYBOARD_VIDEO['value']) {
$PluginShortplayDramaStoryboard->video = $PluginModelTask->result->video_path;
$PluginShortplayDramaStoryboard->use_material_type = 'video';
}
$PluginShortplayDramaStoryboard->save();
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $id])->with(['actors', 'sceneFind', 'props', 'dialogues'])->find();
return $this->resData($PluginShortplayDramaStoryboard);
}
public function CharacterLook(Request $request)
{
$actor_id = $request->post('actor_id');
$drama_id = $request->post('drama_id');
$episode_id = $request->post('episode_id');
$storyboard_id = $request->post('storyboard_id');
$character_look_id = $request->post('character_look_id');
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$PluginShortplayDramaEpisode = PluginShortplayDramaEpisode::where(['id' => $episode_id, 'drama_id' => $PluginShortplayDrama->id])->find();
if (!$PluginShortplayDramaEpisode) {
return $this->fail('分集不存在');
}
$PluginShortplayDramaStoryboard = PluginShortplayDramaStoryboard::where(['id' => $storyboard_id, 'drama_id' => $drama_id, 'episode_id' => $episode_id])->find();
if (!$PluginShortplayDramaStoryboard) {
return $this->fail('分镜不存在');
}
$PluginShortplayDramaStoryboardActor = PluginShortplayDramaStoryboardActor::where(['storyboard_id' => $PluginShortplayDramaStoryboard->id, 'actor_id' => $actor_id])->find();
if (!$PluginShortplayDramaStoryboardActor) {
return $this->fail('演员不存在');
}
$PluginShortplayActorCharacterLook = PluginShortplayActorCharacterLook::where(['id' => $character_look_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayActorCharacterLook) {
return $this->fail('演员妆容不存在');
}
$PluginShortplayDramaStoryboardActor->character_look_id = $PluginShortplayActorCharacterLook->id;
$PluginShortplayDramaStoryboardActor->headimg = $PluginShortplayActorCharacterLook->headimg;
$PluginShortplayDramaStoryboardActor->three_view_image = $PluginShortplayActorCharacterLook->three_view_image;
$PluginShortplayDramaStoryboardActor->save();
return $this->success('设置成功');
}
public function downloadPackage(Request $request)
{
$drama_id = $request->get('drama_id');
$PluginShortplayDrama = PluginShortplayDrama::where(['id' => $drama_id, 'uid' => $request->uid])->find();
if (!$PluginShortplayDrama) {
return $this->fail('短剧不存在');
}
$episode_id = $request->get('episode_id');
$PluginShortplayDramaEpisode = PluginShortplayDramaEpisode::where(['id' => $episode_id, 'drama_id' => $PluginShortplayDrama->id])->find();
if (!$PluginShortplayDramaEpisode) {
return $this->fail('分集不存在');
}
# 初始化打包
$uuid = uniqid();
$data = [
'uuid' => $uuid,
'uid' => $PluginShortplayDrama->uid,
'channels_uid' => $PluginShortplayDrama->channels_uid,
'drama_id' => $PluginShortplayDrama->id,
'episode_id' => $PluginShortplayDramaEpisode->id,
'url' => $request->header('x-forwarded-proto') . '://' . $request->host() . '/app/shortplay/download/storyboard?uuid=' . $uuid,
];
Coroutine::create(function () use ($data) {
try {
sleep(2);
$PluginShortplayDramaStoryboards = PluginShortplayDramaStoryboard::where(['drama_id' => $data['drama_id'], 'episode_id' => $data['episode_id']])->with(['dialogues'])->order('sort', 'asc')->select();
$storyboardSum = count($PluginShortplayDramaStoryboards);
$zip = new \ZipArchive();
$filename = $data['drama_id'] . '_' . $data['episode_id'] . '.zip';
$filepath = runtime_path('temp/storyboard/');
if (!is_dir($filepath)) {
mkdir($filepath, 0777, true);
}
if (!$zip->open($filepath . $filename, \ZipArchive::CREATE | \ZipArchive::OVERWRITE)) {
throw new \Exception('打包失败');
}
$localFiles = [];
# 打包图片素材
$index = 0;
foreach ($PluginShortplayDramaStoryboards as $PluginShortplayDramaStoryboard) {
if (!empty($PluginShortplayDramaStoryboard->image)) {
$localfile = Uploads::downloadTemp($PluginShortplayDramaStoryboard->image);
if (!file_exists($localfile)) {
throw new \Exception('图片素材下载失败');
}
$localFiles[] = $localfile;
$ext = pathinfo($localfile, PATHINFO_EXTENSION);
$zip->addFile($localfile, $PluginShortplayDramaStoryboard->sort . '/image.' . $ext);
$progress = 5 + round(($index / $storyboardSum) * 10);
$index++;
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'action' => 'process', 'index' => 1, 'progress' => $progress]);
}
}
# 打包视频素材
$index = 0;
foreach ($PluginShortplayDramaStoryboards as $PluginShortplayDramaStoryboard) {
if (!empty($PluginShortplayDramaStoryboard->video)) {
$localfile = Uploads::downloadTemp($PluginShortplayDramaStoryboard->video);
if (!file_exists($localfile)) {
throw new \Exception('视频素材下载失败');
}
$ext = pathinfo($localfile, PATHINFO_EXTENSION);
$zip->addFile($localfile, $PluginShortplayDramaStoryboard->sort . '/video.' . $ext);
$localFiles[] = $localfile;
$progress = 15 + round(($index / $storyboardSum) * 40);
$index++;
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'action' => 'process', 'index' => 2, 'progress' => $progress]);
}
}
# 打包对话素材
$srtContent = [];
/**
* 毫秒 → SRT 时间格式
*/
function msToSrtTime(int $ms): string
{
$hours = floor($ms / 3600000);
$minutes = floor(($ms % 3600000) / 60000);
$seconds = floor(($ms % 60000) / 1000);
$milliseconds = $ms % 1000;
return sprintf(
'%02d:%02d:%02d,%03d',
$hours,
$minutes,
$seconds,
$milliseconds
);
}
$index = 1; // SRT 序号必须从 1 开始
$currentTimeline = 0; // 全局时间轴(毫秒)
foreach ($PluginShortplayDramaStoryboards as $storyboard) {
if (empty($storyboard->dialogues)) {
continue;
}
$dialogueText = '';
$maxEnd = 0;
foreach ($storyboard->dialogues as $dialogue) {
// 当前字幕在全局时间轴中的开始 / 结束时间
$startMs = $currentTimeline + $dialogue->start_time;
$endMs = $currentTimeline + $dialogue->end_time;
$temp =
$index . "\n" .
msToSrtTime($startMs) . " --> " . msToSrtTime($endMs) . "\n" .
trim($dialogue->content) . "\n";
$dialogueText .= $temp . "\n";
$srtContent[] = $temp;
$index++;
$maxEnd = max($maxEnd, $endMs);
}
$currentTimeline += $maxEnd;
if (!empty($dialogueText)) {
$zip->addFromString($storyboard->sort . '/对话.txt', $dialogueText);
}
}
// 最终 SRT 文本
$srtText = implode("\n", $srtContent);
$zip->addFromString('字幕.srt', $srtText);
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'action' => 'process', 'index' => 3, 'progress' => 55]);
# 打包音频素材
$index = 0;
foreach ($PluginShortplayDramaStoryboards as $PluginShortplayDramaStoryboard) {
if (empty($PluginShortplayDramaStoryboard->dialogues)) {
continue;
}
$n = 0;
foreach ($PluginShortplayDramaStoryboard->dialogues as $PluginShortplayDramaStoryboardDialogue) {
if (!empty($PluginShortplayDramaStoryboardDialogue->audio)) {
$localfile = Uploads::downloadTemp($PluginShortplayDramaStoryboardDialogue->audio);
if (!file_exists($localfile)) {
throw new \Exception('音频素材下载失败');
}
$ext = pathinfo($localfile, PATHINFO_EXTENSION);
$zip->addFile($localfile, $PluginShortplayDramaStoryboard->sort . "/{$n}-{$PluginShortplayDramaStoryboardDialogue->actor->name}." . $ext);
$localFiles[] = $localfile;
}
$n++;
}
$progress = 55 + round(($index / $storyboardSum) * 10);
$index++;
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'action' => 'process', 'index' => 4, 'progress' => $progress]);
}
# 打包旁白素材
foreach ($PluginShortplayDramaStoryboards as $PluginShortplayDramaStoryboard) {
if (!empty($PluginShortplayDramaStoryboard->narration)) {
$zip->addFromString($PluginShortplayDramaStoryboard->sort . '/旁白.txt', $PluginShortplayDramaStoryboard->narration);
}
}
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'action' => 'process', 'index' => 5, 'progress' => 75]);
# 打包旁白音频素材
$index = 0;
foreach ($PluginShortplayDramaStoryboards as $PluginShortplayDramaStoryboard) {
if (!empty($PluginShortplayDramaStoryboard->narration_audio)) {
$localfile = Uploads::downloadTemp($PluginShortplayDramaStoryboard->narration_audio);
if (!file_exists($localfile)) {
throw new \Exception('旁白音频素材下载失败');
}
$ext = pathinfo($localfile, PATHINFO_EXTENSION);
$zip->addFile($localfile, $PluginShortplayDramaStoryboard->sort . '/旁白.' . $ext);
$localFiles[] = $localfile;
}
$progress = 75 + round(($index / $storyboardSum) * 10);
$index++;
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'action' => 'process', 'index' => 6, 'progress' => $progress]);
}
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'action' => 'process', 'index' => 7, 'progress' => 95]);
$zip->close();
$cacheData = [
'uuid' => $data['uuid'],
'filename' => $filename,
'filepath' => $filepath,
];
Cache::set('downloadPackage_' . $data['uuid'], $cacheData, 60 * 60 * 24);
foreach ($localFiles as $localFile) {
if (file_exists($localFile)) {
unlink($localFile);
}
}
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'action' => 'process', 'index' => 7, 'progress' => 100, 'url' => $data['url'], 'filename' => $filename]);
} catch (\Throwable | ClientException | RequestException $th) {
Log::error("下载打包异常:" . $th->getMessage(), $th->getTrace());
Push::send([
'uid' => $data['uid'],
'channels_uid' => $data['channels_uid'],
'event' => 'downloadPackage',
], ['uuid' => $data['uuid'], 'msg' => $th->getMessage()]);
}
});
return $this->resData(['uuid' => $uuid]);
}
}