李帅

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

......@@ -33,7 +33,7 @@ class AdminMakeVideoController extends AdminController
$grid->column('title','标题');
$grid->column('content','有感');
$grid->column('url')->display(function ($url){
return "<a target='_blank' href='". Storage::disk('public')->url(Str::after($url,'/public')) ."'>查看</a>";
return "<a target='_blank' href='". $url ."'>查看</a>";
});
$grid->column('type','类型')->using([1 => '音频', 2 => '视频']);
$grid->column('duration');
......@@ -41,7 +41,12 @@ class AdminMakeVideoController extends AdminController
$grid->column('poem_id');
$grid->column('temp_id');
$grid->column('thumbnail')->image();
$grid->column('bgm');
$grid->column('bgm')->display(function ($url){
if (Str::of($url)->contains('.mp3'))
return "<a target='_blank' href='". $url ."'>查看</a>";
else
return "<a target='_blank' href='". $url ."'>下载</a>";
});
$grid->column('created_at');
$grid->column('updated_at')->sortable();
......@@ -168,4 +173,17 @@ class AdminMakeVideoController extends AdminController
return $this->form()->response()->refresh()->success(trans('admin.save_succeeded'));
}
public function destroy($id)
{
$immerse = Immerse::query()->find($id);
Storage::disk('public')->delete($immerse->url);
Storage::disk('public')->delete($immerse->thumbnail);
Storage::disk('public')->delete($immerse->bgm);
dd($id);
// return $this->form()->destroy($id);
}
}
......
......@@ -893,6 +893,18 @@ class DevFFmpeg extends Command
return rtrim($drawtext,', ');
}
public function calcFontSize($width, $content)
{
$max_len = 1;
foreach (explode("\n",$content) as $item){
if (mb_strlen($item) > $max_len){
$max_len = mb_strlen($item);
}
}
return ceil(800 * $width / 100 / $max_len);
}
}
......
......@@ -8,6 +8,7 @@ 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;
......@@ -49,67 +50,56 @@ class ImmerseController extends Controller
$validated = $validator->validated();
if (Str::contains($validated['item_url'],'//')){
$item_url = '' ;
}elseif (Str::contains($validated['item_url'],'/storage/app/public/')){
$item_url = $validated['item_url'];
}else{
$item_url = Storage::disk('public')->path($validated['item_url']);
}
$immerse = Immerse::query()->find($request->item_id);
if ($validated['type'] == 1){
// 图文音频
$create = UserMakeVideo::query()->create([
'poem_id' => $immerse->poem_id,
'type' => $immerse->type,
'video_url' => $item_url,
'image_url' => $immerse->image_url,
'bg_music' => $immerse->bg_music,
'bgm_url' => $immerse->bgm_url,
'feel' => $validated['content'],
'weather' => $validated['weather'],
'temp_id' => $immerse->temp_id,
'thumbnail' => $validated['thumbnail_url'] ? 1 : 0,
'thumbnail_url' => $validated['thumbnail_url'],
$create = Immerse::query()->create([
'user_id' => Auth::user()->getAuthIdentifier(),
'title' => '',
'content' => $validated['content'],
'url' => '',
'type' => $validated['type'],
'duration' => 0,
'size' => 0,
'poem_id' => $immerse->poem_id,
'temp_id' => $immerse->temp_id,
'thumbnail' => $immerse->thumbnail,
'bgm' => $validated['item_url'],
]);
// 添加至队列
MakeImages::dispatch($create);
}else{
// 视频
$create = UserMakeVideo::query()->create([
'poem_id' => $immerse->poem_id,
'type' => $immerse->type,
'video_url' => $item_url,
'image_url' => $immerse->image_url,
'bg_music' => $immerse->bg_music,
'bgm_url' => $immerse->bgm_url,
'feel' => $validated['content'],
'weather' => $validated['weather'],
'temp_id' => $immerse->temp_id,
'thumbnail' => $validated['thumbnail_url'] ? 1 : 0,
'thumbnail_url' => $validated['thumbnail_url'],
$create = Immerse::query()->create([
'user_id' => Auth::user()->getAuthIdentifier(),
'title' => '',
'content' => $validated['content'],
'url' => '',
'type' => $validated['type'],
'duration' => 0,
'size' => 0,
'poem_id' => $immerse->poem_id,
'temp_id' => $immerse->temp_id,
'thumbnail' => '',
'bgm' => $immerse->bgm,
]);
// 添加至队列
MakeVideo::dispatch($create);
MakeVideo::dispatch($create, $validated['item_url']);
}
return Response::created();
return Response::created($create->id);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function show($id)
{
//
return Response::success(Immerse::query()->find($id));
}
/**
......
......@@ -4,6 +4,7 @@ namespace App\Http\Controllers\V1;
use App\Http\Controllers\Controller;
use App\Jobs\SendVerificationMessage;
use App\Models\Immerse;
use App\Models\User;
use App\Models\UserProfile;
use Illuminate\Http\Request;
......@@ -136,4 +137,27 @@ class UserController extends Controller
return Response::success($user);
}
public function videos(Request $request)
{
$type = $request->get('type');
$user_id = Auth::user()->getAuthIdentifier();
$immerse = Immerse::query()->where('user_id', 1);
switch ($type)
{
case 0 : // 审核中...
$data = $immerse->where('is_check', 0)->get();
break;
case 1 : // 审核通过...
$data = $immerse->where('is_check',1)->get();
break;
default: // 全部
$data = $immerse->get();
break;
}
return Response::success($data);
}
}
......
......@@ -80,14 +80,14 @@ class MakeImages implements ShouldQueue
'user_id' => 1,
'title' => '',
'content' => $this->adminMakeVideo->feel,
'url' => $output,
'url' => str_replace(Storage::disk('public')->path(''),'',$output),
'type' => $this->adminMakeVideo->type == 1 ? 2 : 1,
'duration' => 0,
'size' => $video_info['format']['size'],
'poem_id' => $this->adminMakeVideo->poem_id,
'temp_id' => $this->adminMakeVideo->temp_id,
'thumbnail' => '',
'bgm' => $this->adminMakeVideo->bgm_url,
'thumbnail' => str_replace(Storage::disk('public')->path(''),'',$image),
'bgm' => str_replace(Storage::disk('public')->path(''),'',$this->adminMakeVideo->bgm_url),
];
}else{
......@@ -145,8 +145,6 @@ class MakeImages implements ShouldQueue
if (!$this->execmd($cmd)) return;
// 全部合成以后创建 临境
$video_info = $this->mediainfo($output);
......@@ -154,14 +152,14 @@ class MakeImages implements ShouldQueue
'user_id' => 1,
'title' => '',
'content' => $this->adminMakeVideo->feel,
'url' => $output,
'url' => str_replace(Storage::disk('public')->path(''),'',$output),
'type' => $this->adminMakeVideo->type == 1 ? 2 : 1,
'duration' => $video_info['format']['duration'],
'size' => $video_info['format']['size'],
'poem_id' => $this->adminMakeVideo->poem_id,
'temp_id' => $this->adminMakeVideo->temp_id,
'thumbnail' => '',
'bgm' => $this->adminMakeVideo->bgm_url,
'thumbnail' => str_replace(Storage::disk('public')->path(''),'',$this->adminMakeVideo->images_url),
'bgm' => str_replace(Storage::disk('public')->path(''),'',$this->adminMakeVideo->bgm_url),
];
}
......
......@@ -161,35 +161,28 @@ class MakeVideo implements ShouldQueue
if (!$this->execmd($cmd)) return;
// $video = $this->getTempPath();
// if ( $adminMakeVideo->thumbnail == 1 && $adminMakeVideo->thumbnail_url){
// $thumbnail = Storage::disk('public')->path($adminMakeVideo->thumbnail_url);
// }else{
// $thumbnail = $last_frame_video;
// }
// $cmd = $this->ffmpeg. ' -y'.
// ' -i ' . escapeshellarg($video_temp).
// ' -i ' . escapeshellarg($thumbnail).
// ' -map 1 -map 0 -c copy -disposition:0 attached_pic '.
// escapeshellarg($video);
// $this->execmd($cmd);
// 全部合成以后创建 临境
$video_info = $this->mediainfo($video);
Immerse::query()->create([
'user_id' => 1,
'title' => '',
'content' => $this->adminMakeVideo->feel,
'url' => $video,
'type' => $this->adminMakeVideo->type == 1 ? 2 : 1,
'duration' => $video_info['format']['duration'],
'size' => $video_info['format']['size'],
'poem_id' => $this->adminMakeVideo->poem_id,
'temp_id' => $this->adminMakeVideo->temp_id,
'thumbnail' => $thumbnail,
'bgm' => $this->adminMakeVideo->bgm_url,
]);
try{
// 全部合成以后创建 临境
$video_info = $this->mediainfo($video);
Immerse::query()->create([
'user_id' => 1,
'title' => '',
'content' => $this->adminMakeVideo->feel,
'url' => str_replace(Storage::disk('public')->path(''),'',$video),
'type' => $this->adminMakeVideo->type == 1 ? 2 : 1,
'duration' => $video_info['format']['duration'],
'size' => $video_info['format']['size'],
'poem_id' => $this->adminMakeVideo->poem_id,
'temp_id' => $this->adminMakeVideo->temp_id,
'thumbnail' => '',
'state' => 1,
'bgm' => $this->adminMakeVideo->bgm_url ?? '',
]);
}catch (\Exception $exception){
echo $exception->getMessage();
}
}
......@@ -605,7 +598,7 @@ class MakeVideo implements ShouldQueue
switch ($component->name){
case 'one_poem':
$content = $this->adminMakeVideo->poem->content;
$text_file = $this->getTempPath('txt');
$text_file = $this->getTempPath('.txt');
file_put_contents($text_file, $content);
$text_color = $component->text_color ?? 'white';
......@@ -690,6 +683,6 @@ class MakeVideo implements ShouldQueue
}
}
return ceil($this->width * $width / 10 / $max_len);
return ceil($this->width * $width / 100 / $max_len);
}
}
......
......@@ -12,13 +12,17 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class UserMakeImages implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $adminMakeVideo;
public $debug = true;
public $immerse;
protected $ffmpeg;
......@@ -32,12 +36,12 @@ class UserMakeImages implements ShouldQueue
/**
* Create a new job instance.
* @param AdminMakeVideo $adminMakeVideo
* @param Immerse $immerse
* @return void
*/
public function __construct(AdminMakeVideo $adminMakeVideo)
public function __construct(Immerse $immerse)
{
$this->adminMakeVideo = $adminMakeVideo;
$this->immerse = $immerse;
$this->ffmpeg = env('FFMPEG_CMD');
$this->ffprobe = env('FFPROBE_CMD');
......@@ -52,16 +56,16 @@ class UserMakeImages implements ShouldQueue
public function handle()
{
$watermark = Storage::disk('public')->path('ffmpeg/LOGO_eng.png');
$image = Storage::disk('public')->path($this->adminMakeVideo->images_url);
$image = Storage::disk('public')->path($this->immerse->thumbnail);
if (!File::exists($image)) return;
if ($this->debug) Log::debug('image url :' . $image);
$media_info = $this->mediainfo($image);
$this->width = $width = $media_info['streams'][0]['width'];
$this->height = $height = $media_info['streams'][0]['height'];
if ($this->adminMakeVideo->type == 2 && $this->adminMakeVideo->bg_music == 0){
if ($this->immerse->type == 2 && $this->immerse->bgm){
// 没有背景音,单图一张,输出为单图。
$output = $this->getTempPath('.png',false);
$cmd = $this->ffmpeg . ' -y '.
' -i ' . escapeshellarg($image).
' -i ' . escapeshellarg($watermark).
......@@ -75,22 +79,9 @@ class UserMakeImages implements ShouldQueue
// 全部合成以后创建 临境
$video_info = $this->mediainfo($output);
$create = [
'user_id' => 1,
'title' => '',
'content' => $this->adminMakeVideo->feel,
'url' => $output,
'type' => $this->adminMakeVideo->type == 1 ? 2 : 1,
'duration' => 0,
'size' => $video_info['format']['size'],
'poem_id' => $this->adminMakeVideo->poem_id,
'temp_id' => $this->adminMakeVideo->temp_id,
'thumbnail' => '',
'bgm' => $this->adminMakeVideo->bgm_url,
];
}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');
......@@ -100,10 +91,6 @@ class UserMakeImages implements ShouldQueue
// 生成贴纸和签名
$end_wallpaper = $this->wallpaperWithSignature($end_wallpaper, $thumbnail, $signature, $font);
// 有背景音 单图合成视频,时长为音频时长,音频加入背景音
$bgm = Storage::disk('public')->path($this->adminMakeVideo->bgm_url);
// 制作最后一帧
$size = $this->width . 'x' . $this->height;
$time_length = 0.7;
......@@ -127,7 +114,6 @@ class UserMakeImages implements ShouldQueue
$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 ' .
......@@ -145,27 +131,15 @@ class UserMakeImages implements ShouldQueue
if (!$this->execmd($cmd)) return;
// 全部合成以后创建 临境
$video_info = $this->mediainfo($output);
$create = [
'user_id' => 1,
'title' => '',
'content' => $this->adminMakeVideo->feel,
'url' => $output,
'type' => $this->adminMakeVideo->type == 1 ? 2 : 1,
'duration' => $video_info['format']['duration'],
'size' => $video_info['format']['size'],
'poem_id' => $this->adminMakeVideo->poem_id,
'temp_id' => $this->adminMakeVideo->temp_id,
'thumbnail' => '',
'bgm' => $this->adminMakeVideo->bgm_url,
];
}
Immerse::query()->create($create);
$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'],
]);
}
/***
......
......@@ -20,7 +20,9 @@ class UserMakeVideo implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $adminMakeVideo;
public $immerse;
public $resource_url;
protected $ffmpeg;
......@@ -32,12 +34,14 @@ class UserMakeVideo implements ShouldQueue
/**
* Create a new job instance.
* @param AdminMakeVideo $adminMakeVideo
* @param Immerse $immerse
* @return void
*/
public function __construct(\App\Models\UserMakeVideo $adminMakeVideo)
public function __construct(Immerse $immerse, $resource_url)
{
$this->adminMakeVideo = $adminMakeVideo;
$this->immerse = $immerse;
$this->resource_url = $resource_url;
$this->ffmpeg = env('FFMPEG_CMD');
$this->ffprobe = env('FFPROBE_CMD');
......@@ -51,10 +55,9 @@ class UserMakeVideo implements ShouldQueue
*/
public function handle()
{
$adminMakeVideo = $this->adminMakeVideo;
$file = Storage::disk('public')->path($adminMakeVideo->video_url);
$is_bgm = $adminMakeVideo->bg_music;
$bgm = Storage::disk('public')->path($adminMakeVideo->bgm_url);
$file = Storage::disk('public')->path($this->resource_url);
$is_bgm = isset($this->immerse->bgm);
$bgm = Storage::disk('public')->path($this->immerse->bgm);
// 1.getmediainfo 记录时长,音频视频取最长。
$cmd = $this->ffprobe . ' -v quiet -print_format json -show_format -show_streams ' . escapeshellarg($file);
......@@ -66,7 +69,7 @@ class UserMakeVideo implements ShouldQueue
}
/** 记录媒体信息时长*/
$media_file_time_length = isset($media_info['format']['duration']) ? $media_info['format']['duration'] : 0;
$media_file_time_length = isset($media_info['format']['duration']) ?: 0;
if ($media_info['streams'][0]['codec_type'] !== 'video') {
Log::channel('daily')->error('视频没有video track');
return;
......@@ -120,7 +123,7 @@ class UserMakeVideo implements ShouldQueue
$thumbnail = Storage::disk('public')->path('ffmpeg') . "/thumbnail.png";
$font = Storage::disk('public')->path('ffmpeg') . "/arialuni.ttf";
$user = User::query()->find($this->adminMakeVideo->user_id);
$user = User::query()->find($this->immerse->user_id);
$signature = $user->nickname;
// 生成贴纸和签名
......@@ -164,36 +167,17 @@ class UserMakeVideo implements ShouldQueue
if (!$this->execmd($cmd)) return;
// $video = $this->getTempPath();
// if ( $adminMakeVideo->thumbnail == 1 && $adminMakeVideo->thumbnail_url){
// $thumbnail = Storage::disk('public')->path($adminMakeVideo->thumbnail_url);
// }else{
// $thumbnail = $last_frame_video;
// }
// $cmd = $this->ffmpeg. ' -y'.
// ' -i ' . escapeshellarg($video_temp).
// ' -i ' . escapeshellarg($thumbnail).
// ' -map 1 -map 0 -c copy -disposition:0 attached_pic '.
// escapeshellarg($video);
// $this->execmd($cmd);
// 全部合成以后创建 临境
$video_info = $this->mediainfo($video);
Immerse::query()->create([
'user_id' => $this->adminMakeVideo->user_id,
'title' => '',
'content' => $this->adminMakeVideo->feel,
'url' => $video,
'type' => $this->adminMakeVideo->type == 1 ? 2 : 1,
'duration' => $video_info['format']['duration'],
$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'],
'poem_id' => $this->adminMakeVideo->poem_id,
'temp_id' => $this->adminMakeVideo->temp_id,
'thumbnail' => $thumbnail,
'bgm' => $this->adminMakeVideo->bgm_url,
]);
return;
}
/**
......@@ -598,7 +582,7 @@ class UserMakeVideo 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');
......@@ -607,7 +591,7 @@ class UserMakeVideo 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);
......@@ -659,7 +643,7 @@ class UserMakeVideo implements ShouldQueue
'box=1:boxcolor=' . $text_bg_color . '@' . $opacity . '", ';
break;
case 'feel':
$content = $this->adminMakeVideo->feel;
$content = $this->immerse->content;
$text_color = $component->text_color ?? 'white';
$text_bg_color = $component->text_bg_color ?? '0xd0cdcc';
$opacity = $component->opacity ? $component->opacity / 100 : '0.5';
......
......@@ -24,34 +24,38 @@ class Immerse extends Model
{
if ($url == '') return $url;
if (Str::contains($url, '/storage/app/public/')) {
$str = Str::of($url)->replace('/usr/local/nginx/html/OnePoem/storage/app/public/', '');
return Storage::disk('public')->url($str);
} else {
return Storage::disk('public')->url($url);
}
return Storage::disk('public')->url($url);
}
public function getThumbnailAttribute($url)
{
if (Str::contains($url, '/storage/app/public/')) {
$str = Str::of($url)->replace('/usr/local/nginx/html/OnePoem/storage/app/public/', '');
return Storage::disk('public')->url($str);
} else {
return Storage::disk('public')->url($url);
}
if ($url == '') return $url;
return Storage::disk('public')->url($url);
}
public function getBgmAttribute($url)
{
if ($url == '') return $url;
if (Str::contains($url, '/storage/app/public/')) {
$str = Str::of($url)->replace('/usr/local/nginx/html/OnePoem/storage/app/public/', '');
return Storage::disk('public')->url($str);
} else {
return Storage::disk('public')->url($url);
}
return Storage::disk('public')->url($url);
}
public function poem()
{
return $this->hasOne(OnePoem::class,'id','poem_id');
}
public function temp()
{
return $this->hasOne(VideoTemp::class,'id','temp_id');
}
public function getContentPosition($field = 'one_poem')
{
$component = $this->temp()->first()->components()->where('name','=',$field)->first();
return VideoTemp::POSITION_FFMPEG[$component->position];
}
}
......
......@@ -27,7 +27,7 @@ class CreateImmerseTable extends Migration
$table->integer('collect')->default(0)->comment('收藏量');
$table->integer('share')->default(0)->comment('分享量');
$table->integer('comment')->default(0)->comment('评论数');
$table->unsignedTinyInteger('is_self')->index()->default(1)->comment('自制=1,搬运=2');
$table->unsignedTinyInteger('state')->index()->default(1)->comment('0=正在合成,1=已完成,2=合成失败');
$table->unsignedTinyInteger('is_publish')->index()->default(1)->comment('草稿=0,发布=1');
$table->unsignedTinyInteger('is_check')->index()->default(0)->comment('审核通过=1,未通过=0');
$table->timestamps();
......
......@@ -41,7 +41,7 @@ Route::prefix('v1')->namespace('App\Http\Controllers\V1')->group(function (Route
$api->get('/user', 'UserController@user')->middleware('auth:sanctum');
/** 上传作品 */
$api->get('/user', 'UserController@user')->middleware('auth:sanctum');
$api->get('/my/videos', 'UserController@videos')->middleware('auth:sanctum');
/** 临境 */
......