李帅

1.后台可上传图文内容。

......@@ -181,9 +181,8 @@ class AdminMakeVideoController extends AdminController
Storage::disk('public')->delete($immerse->url);
Storage::disk('public')->delete($immerse->thumbnail);
Storage::disk('public')->delete($immerse->bgm);
$immerse->delete();
dd($id);
// return $this->form()->destroy($id);
return $this->form()->response()->refresh()->success('删除成功');
}
}
......
......@@ -4,14 +4,11 @@ namespace App\Http\Controllers\V1;
use App\Http\Controllers\Controller;
use App\Models\Immerse;
use App\Models\UserMakeVideo;
use App\Jobs\UserMakeVideo as MakeVideo;
use App\Jobs\UserMakeImages as MakeImages;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Jiannei\Response\Laravel\Support\Facades\Response;
class ImmerseController extends Controller
......@@ -41,7 +38,7 @@ class ImmerseController extends Controller
'type' => 'required',
'content' => 'sometimes',
'weather' => 'sometimes',
'thumbnail_url' => 'sometimes',
'thumbnail' => 'sometimes',
]);
if ($validator->fails()){
......@@ -50,10 +47,12 @@ class ImmerseController extends Controller
$validated = $validator->validated();
$user_id = $request->user()->id;
$immerse = Immerse::query()->find($request->item_id);
if ($validated['type'] == 1){
$create = Immerse::query()->create([
'user_id' => Auth::user()->getAuthIdentifier(),
'user_id' => $user_id,
'title' => '',
'content' => $validated['content'],
'url' => '',
......@@ -64,6 +63,7 @@ class ImmerseController extends Controller
'temp_id' => $immerse->temp_id,
'thumbnail' => $immerse->thumbnail,
'bgm' => $validated['item_url'],
'state' => 0
]);
// 添加至队列
......@@ -71,7 +71,7 @@ class ImmerseController extends Controller
}else{
// 视频
$create = Immerse::query()->create([
'user_id' => Auth::user()->getAuthIdentifier(),
'user_id' => $user_id,
'title' => '',
'content' => $validated['content'],
'url' => '',
......@@ -82,13 +82,14 @@ class ImmerseController extends Controller
'temp_id' => $immerse->temp_id,
'thumbnail' => '',
'bgm' => $immerse->bgm,
'state' => 0
]);
// 添加至队列
MakeVideo::dispatch($create, $validated['item_url']);
}
return Response::created($create->id);
return Response::created($create);
}
/**
......
......@@ -144,7 +144,7 @@ class UserController extends Controller
$user_id = Auth::user()->getAuthIdentifier();
$immerse = Immerse::query()->where('user_id', 1);
$immerse = Immerse::query()->where('user_id', $user_id);
switch ($type)
{
case 0 : // 审核中...
......
......@@ -2,7 +2,6 @@
namespace App\Jobs;
use App\Models\AdminMakeVideo;
use App\Models\Immerse;
use App\Models\VideoTemp;
use Carbon\Carbon;
......@@ -15,6 +14,7 @@ use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class UserMakeImages implements ShouldQueue
{
......@@ -63,83 +63,88 @@ class UserMakeImages implements ShouldQueue
$this->width = $width = $media_info['streams'][0]['width'];
$this->height = $height = $media_info['streams'][0]['height'];
if ($this->immerse->type == 2 && $this->immerse->bgm){
// 没有背景音,单图一张,输出为单图。
$output = $this->getTempPath('.png',false);
$cmd = $this->ffmpeg . ' -y '.
' -i ' . escapeshellarg($image).
' -i ' . escapeshellarg($watermark).
' -filter_complex "[0:0] ' .
$this->getTextContentString().
' [text];[text]'.
' [1:0]overlay=20:20" ' .
escapeshellarg($output);
if (!$this->execmd($cmd)) return;
// 全部合成以后创建 临境
$video_info = $this->mediainfo($output);
}else{
// 有背景音 单图合成视频,时长为音频时长,音频加入背景音
$bgm = Storage::disk('public')->path($this->immerse->bgm);
$end_wallpaper = Storage::disk('public')->path('ffmpeg/end_wallpaper.png');
$thumbnail = Storage::disk('public')->path('ffmpeg/thumbnail.png');
$font = Storage::disk('public')->path('ffmpeg') . "/arialuni.ttf";
$signature = "一言 · 官方出品";
// 生成贴纸和签名
$end_wallpaper = $this->wallpaperWithSignature($end_wallpaper, $thumbnail, $signature, $font);
// 制作最后一帧
$size = $this->width . 'x' . $this->height;
$time_length = 0.7;
$r = 24;
$last_frame_video = $this->getTempPath('.mp4');
$font = Storage::disk('public')->path('ffmpeg/arialuni.ttf');
$cmd = $this->ffmpeg . ' -y -i ' . escapeshellarg($image) .
' -i ' . escapeshellarg($watermark) .
" -f lavfi -i nullsrc=s={$size}:d={$time_length}:r={$r} -f lavfi -i aevalsrc=0:duration={$time_length}" .
' -filter_complex "'.
' [0:0] ' . $this->getTextContentString() .
'[text];[text][1:0]overlay=20:20[water];' .
' [water]select=\'eq(n,0)\',setpts=PTS-STARTPTS[lastframe];[2:v][lastframe]overlay[v] " ' .
' -map [v] -map 3:a ' . escapeshellarg($last_frame_video);
if (!$this->execmd($cmd)) return;
// 利用最后一帧制作动画
$signature_x = 0;
$signature_y = -20;
$animate = $this->makeAnimate($last_frame_video, $end_wallpaper, '', $signature_x, $signature_y, $font);
$output = $this->getTempPath('.mp4',false);
$cmd = $this->ffmpeg . ' -y ' .
' -i ' . escapeshellarg($image).
' -i ' . escapeshellarg($watermark).
' -i ' . escapeshellarg($bgm) .
' -i ' . escapeshellarg($animate) .
' -filter_complex "[0:0] ' . $this->getTextContentString().
'[text];[text][1:0]overlay=20:20[water];' .
'[water][2:a][3:v][3:a]concat=n=2:v=1:a=1[v][a]" '.
' -map [v] -map [a] '.
' -c:v libx264 -bt 256k -r 25' .
' -ar 44100 -ac 2 -qmin 30 -qmax 60 -profile:v baseline -preset fast '
. escapeshellarg($output);
if (!$this->execmd($cmd)) return;
$video_info = $this->mediainfo($output);
}
try{
if ($this->immerse->bgm == '' || $this->immerse->bgm == null) {
// 没有背景音,单图一张,输出为单图。
$output = $this->getTempPath('.png',false);
$cmd = $this->ffmpeg . ' -y '.
' -i ' . escapeshellarg($image).
' -i ' . escapeshellarg($watermark).
' -filter_complex "[0:0] ' .
$this->getTextContentString().
' [text];[text]'.
' [1:0]overlay=20:20" ' .
escapeshellarg($output);
if (!$this->execmd($cmd)) return;
// 全部合成以后创建 临境
$video_info = $this->mediainfo($output);
}else{
// 有背景音 单图合成视频,时长为音频时长,音频加入背景音
$bgm = Storage::disk('public')->path($this->immerse->bgm);
$end_wallpaper = Storage::disk('public')->path('ffmpeg/end_wallpaper.png');
$thumbnail = Storage::disk('public')->path('ffmpeg/thumbnail.png');
$font = Storage::disk('public')->path('ffmpeg') . "/arialuni.ttf";
$signature = "一言 · 官方出品";
// 生成贴纸和签名
$end_wallpaper = $this->wallpaperWithSignature($end_wallpaper, $thumbnail, $signature, $font);
// 制作最后一帧
$size = $this->width . 'x' . $this->height;
$time_length = 0.7;
$r = 24;
$last_frame_video = $this->getTempPath('.mp4');
$font = Storage::disk('public')->path('ffmpeg/arialuni.ttf');
$cmd = $this->ffmpeg . ' -y -i ' . escapeshellarg($image) .
' -i ' . escapeshellarg($watermark) .
" -f lavfi -i nullsrc=s={$size}:d={$time_length}:r={$r} -f lavfi -i aevalsrc=0:duration={$time_length}" .
' -filter_complex "'.
' [0:0] ' . $this->getTextContentString() .
'[text];[text][1:0]overlay=20:20[water];' .
' [water]select=\'eq(n,0)\',setpts=PTS-STARTPTS[lastframe];[2:v][lastframe]overlay[v] " ' .
' -map [v] -map 3:a ' . escapeshellarg($last_frame_video);
if (!$this->execmd($cmd)) return;
// 利用最后一帧制作动画
$signature_x = 0;
$signature_y = -20;
$animate = $this->makeAnimate($last_frame_video, $end_wallpaper, '', $signature_x, $signature_y, $font);
$output = $this->getTempPath('.mp4',false);
$cmd = $this->ffmpeg . ' -y ' .
' -i ' . escapeshellarg($image).
' -i ' . escapeshellarg($watermark).
' -i ' . escapeshellarg($bgm) .
' -i ' . escapeshellarg($animate) .
' -filter_complex "[0:0] ' . $this->getTextContentString().
'[text];[text][1:0]overlay=20:20[water];' .
'[water][2:a][3:v][3:a]concat=n=2:v=1:a=1[v][a]" '.
' -map [v] -map [a] '.
' -c:v libx264 -bt 256k -r 25' .
' -ar 44100 -ac 2 -qmin 30 -qmax 60 -profile:v baseline -preset fast '
. escapeshellarg($output);
if (!$this->execmd($cmd)) return;
$video_info = $this->mediainfo($output);
}
$this->immerse->query()->update([
'url' => str_replace(Storage::disk('public')->path(''),'',$output),
'state' => 1,
'duration' => $video_info['format']['duration'] ?? 0,
'size' => $video_info['format']['size'],
]);
$this->immerse->url = Str::of($output)->replace(Storage::disk('public')->path(''), '');
$this->immerse->state = 1;
$this->immerse->duration = $video_info['format']['duration'] ?? 0.00;
$this->immerse->size = $video_info['format']['size'];
$this->immerse->save();
}catch (\Exception $exception){
$this->immerse->state = 2;
$this->immerse->save();
Log::channel('daily')->error($exception->getMessage());
}
}
/***
......@@ -249,7 +254,7 @@ class UserMakeImages implements ShouldQueue
public function getTextContentString()
{
$components = $this->adminMakeVideo->temp()->first()->components()->get();
$components = $this->immerse->temp()->first()->components()->get();
$font = Storage::disk('public')->path('ffmpeg/arialuni.ttf');
......@@ -258,7 +263,7 @@ class UserMakeImages implements ShouldQueue
foreach ($components as $component) {
switch ($component->name){
case 'one_poem':
$content = $this->adminMakeVideo->poem->content;
$content = $this->immerse->poem->content;
$text_file = $this->getTempPath('.txt');
file_put_contents($text_file, $content);
......@@ -310,7 +315,7 @@ class UserMakeImages implements ShouldQueue
'box=1:boxcolor=' . $text_bg_color . '@' . $opacity . '", ';
break;
case 'feel':
$content = $this->adminMakeVideo->feel;
$content = $this->immerse->feel;
$text_color = $component->text_color ?? 'white';
$text_bg_color = $component->text_bg_color ?? '0xd0cdcc';
$opacity = $component->opacity ? $component->opacity / 100 : '0.5';
......
......@@ -15,6 +15,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class UserMakeVideo implements ShouldQueue
{
......@@ -56,7 +57,7 @@ class UserMakeVideo implements ShouldQueue
public function handle()
{
$file = Storage::disk('public')->path($this->resource_url);
$is_bgm = isset($this->immerse->bgm);
$is_bgm = isset($this->immerse->bgm) && $this->immerse->bgm != '';
$bgm = Storage::disk('public')->path($this->immerse->bgm);
// 1.getmediainfo 记录时长,音频视频取最长。
......@@ -119,63 +120,68 @@ class UserMakeVideo implements ShouldQueue
return;
}
$end_wallpaper = Storage::disk('public')->path('ffmpeg') . "/end_wallpaper.png";
$thumbnail = Storage::disk('public')->path('ffmpeg') . "/thumbnail.png";
$font = Storage::disk('public')->path('ffmpeg') . "/arialuni.ttf";
try{
$end_wallpaper = Storage::disk('public')->path('ffmpeg') . "/end_wallpaper.png";
$thumbnail = Storage::disk('public')->path('ffmpeg') . "/thumbnail.png";
$font = Storage::disk('public')->path('ffmpeg') . "/arialuni.ttf";
$user = User::query()->find($this->immerse->user_id);
$signature = $user->nickname ?? $user->email;
// 生成贴纸和签名
$end_wallpaper = $this->wallpaperWithSignature($end_wallpaper, $thumbnail, $signature, $font);
// 截取最后一帧
$last_frame_video = $this->getTempPath();
$this->width = $width = $media_info['streams'][0]['width'];
$height = $media_info['streams'][0]['height'];
$size = $width . 'x' . $height;
$time_length = 0.7;
$r = 24;
$frame_n = $media_info['streams'][0]['nb_frames'] - 2;
$cmd = $this->ffmpeg . ' -y -i ' . escapeshellarg($file) .
" -f lavfi -i nullsrc=s={$size}:d={$time_length}:r={$r} -f lavfi -i aevalsrc=0:duration={$time_length}" .
" -filter_complex \"[0:v]select='eq(n,{$frame_n})',setpts=PTS-STARTPTS[lastframe];[1:v][lastframe]overlay[v]\"" .
' -map [v] -map 2:a ' . escapeshellarg($last_frame_video);
if (!$this->execmd($cmd)) return;
$user = User::query()->find($this->immerse->user_id);
$signature = $user->nickname;
// 生成贴纸和签名
$end_wallpaper = $this->wallpaperWithSignature($end_wallpaper, $thumbnail, $signature, $font);
$signature_x = 0;
$signature_y = -20;
$animate = $this->makeAnimate($last_frame_video, $end_wallpaper, '', $signature_x, $signature_y, $font);
// 截取最后一帧
$last_frame_video = $this->getTempPath();
$this->width = $width = $media_info['streams'][0]['width'];
$height = $media_info['streams'][0]['height'];
$size = $width . 'x' . $height;
$time_length = 0.7;
$r = 24;
$frame_n = $media_info['streams'][0]['nb_frames'] - 2;
$cmd = $this->ffmpeg . ' -y -i ' . escapeshellarg($file) .
" -f lavfi -i nullsrc=s={$size}:d={$time_length}:r={$r} -f lavfi -i aevalsrc=0:duration={$time_length}" .
" -filter_complex \"[0:v]select='eq(n,{$frame_n})',setpts=PTS-STARTPTS[lastframe];[1:v][lastframe]overlay[v]\"" .
' -map [v] -map 2:a ' . escapeshellarg($last_frame_video);
if (!$this->execmd($cmd)) return;
$signature_x = 0;
$signature_y = -20;
$animate = $this->makeAnimate($last_frame_video, $end_wallpaper, '', $signature_x, $signature_y, $font);
$watermark = Storage::disk('public')->path('ffmpeg/LOGO_eng.png');
$video = $this->getTempPath('.mp4',false);
$cmd = $this->ffmpeg . ' -y '.
' -i ' . escapeshellarg($file).
' -i ' . escapeshellarg($animate).
' -i ' . escapeshellarg($watermark).
$audio_input .
' -filter_complex "[0:0] ' .
$this->getTextContentString().
' [text];[text]'.
' [2:v]overlay=20:20[water];[water]' . $audio_filter . '[1:0][1:1] concat=n=2:v=1:a=1[v][a]" ' .
' -map [v] -map [a]'.
' -c:v libx264 -bt 256k -r 25' .
' -ar 44100 -ac 2 -qmin 30 -qmax 60 -profile:v baseline -preset fast ' .
escapeshellarg($video);
if (!$this->execmd($cmd)) return;
// 全部合成以后创建 临境
$video_info = $this->mediainfo($video);
$this->immerse->query()->update([
'url' => str_replace(Storage::disk('public')->path(''),'',$output),
'state' => 1,
'duration' => $video_info['format']['duration'] ?? 0,
'size' => $video_info['format']['size'],
]);
$watermark = Storage::disk('public')->path('ffmpeg/LOGO_eng.png');
$video = $this->getTempPath('.mp4',false);
$cmd = $this->ffmpeg . ' -y '.
' -i ' . escapeshellarg($file).
' -i ' . escapeshellarg($animate).
' -i ' . escapeshellarg($watermark).
$audio_input .
' -filter_complex "[0:0] ' .
$this->getTextContentString().
' [text];[text]'.
' [2:v]overlay=20:20[water];[water]' . $audio_filter . '[1:0][1:1] concat=n=2:v=1:a=1[v][a]" ' .
' -map [v] -map [a]'.
' -c:v libx264 -bt 256k -r 25' .
' -ar 44100 -ac 2 -qmin 30 -qmax 60 -profile:v baseline -preset fast ' .
escapeshellarg($video);
if (!$this->execmd($cmd)) return;
// 全部合成以后创建 临境
$video_info = $this->mediainfo($video);
$this->immerse->url = Str::of($video)->replace(Storage::disk('public')->path(''), '');
$this->immerse->state = 1;
$this->immerse->duration = $video_info['format']['duration'] ?? 0.00;
$this->immerse->size = $video_info['format']['size'];
$this->immerse->save();
}catch (\Exception $exception){
$this->immerse->state = 2;
$this->immerse->save();
Log::channel('daily')->error($exception->getMessage());
}
return;
}
......@@ -677,6 +683,6 @@ class UserMakeVideo implements ShouldQueue
}
}
return ceil($this->width * $width / 10 / $max_len);
return ceil($this->width * $width / 100 / $max_len);
}
}
......
......@@ -27,20 +27,6 @@ class Immerse extends Model
return Storage::disk('public')->url($url);
}
public function getThumbnailAttribute($url)
{
if ($url == '') return $url;
return Storage::disk('public')->url($url);
}
public function getBgmAttribute($url)
{
if ($url == '') return $url;
return Storage::disk('public')->url($url);
}
public function poem()
{
......
......@@ -40,12 +40,12 @@ Route::prefix('v1')->namespace('App\Http\Controllers\V1')->group(function (Route
/** 用户信息*/
$api->get('/user', 'UserController@user')->middleware('auth:sanctum');
/** 上传作品 */
/** 我的作品 */
$api->get('/my/videos', 'UserController@videos')->middleware('auth:sanctum');
/** 临境 */
$api->apiResource('/immersive', 'ImmerseController');
$api->apiResource('/immersive', 'ImmerseController')->middleware('auth:sanctum');
/** 众妙 */
$api->get('/packpoem', 'HomeController@packpoem');
......