watermark_service = new WatermarkService(); $this->video_resolutions_service = new VideoResolutionsService(); } public function handleUpload(Request $request) { $file = $request->file('video'); $filename = $file->getClientOriginalName(); $token = $request->token; if ($request->has('mode') && $request->mode == 'create') { $mode = 'create'; $id = auth()->id(); } else { $mode = 'update'; $id = $request->id; } if ($id) { if ($request->has('tempPath')) { $temp_path = $request->tempPath; } else { $temp_path = $this->getTempLocalStoragePath($id, $token, $mode); } if ($temp_path) { Storage::deleteDirectory($temp_path); Storage::put("$temp_path/filename.dat", $filename); Storage::put("$temp_path/path.dat", $temp_path); $temp_filename = 'video.mp4'; $video_local_url = Storage::putFileAs($temp_path, $file, $temp_filename); $resolution = $this->getVideoResolution(storage_path("app/$video_local_url")); if ($this->isResolutionAllowed($resolution)) { VideoService::copyAndRescale(storage_path("app/$temp_path/$temp_filename"), storage_path("app/$temp_path/video.mp4")); $ffmpeg = FFMpeg::create(); $video = $ffmpeg->open(storage_path("app/{$video_local_url}")); $ffprobe = FFProbe::create(get_ffprobe_config()); $code_ac = $ffprobe->format(storage_path("app/{$video_local_url}"))->get('duration'); // $thumbnails_storage_path = "$temp_path/thumbnails"; // if (! Storage::exists($thumbnails_storage_path)) { // Storage::makeDirectory($thumbnails_storage_path); // } // $thumbnails = []; // $timeFrameInSeconds = 2; // for ($i = 0; $i < 3; $i++) { // if ($timeFrameInSeconds < round($code_ac, PHP_ROUND_HALF_DOWN)) { // $thumbnail_filename = Str::random(10).'.jpg'; // $thumbnail_path = asset("storage/app/$temp_path/thumbnails/$thumbnail_filename"); // $video->frame(\FFMpeg\Coordinate\TimeCode::fromSeconds($timeFrameInSeconds)) // ->save(storage_path("app/$temp_path/thumbnails/$thumbnail_filename")); // $thumbnails[] = [ // 'id' => $thumbnail_filename, // 'thumbnail_url' => $thumbnail_path, // 'filename' => $thumbnail_filename, // 'is_active' => $i === 0, // ]; // $timeFrameInSeconds += 2; // } // } // if ($mode == 'update') { // $studio_plus_video = StudioPlusVideo::find($id); // $current_thumbnails = $studio_plus_video->thumbnails->where('is_uploaded',1)->toArray(); // $thumbnails = array_merge($thumbnails, $current_thumbnails); // } return [ 'temp_path' => $temp_path, 'url' => asset('storage/app/'.$video_local_url), //'thumbnails' => $thumbnails, ]; } else { return 'invalid-resolution'; } } } return false; } public function handleUploadThumbnail(Request $request) { $file = $request->file('thumbnail'); $filename = $file->getClientOriginalName(); $exploded_filename = explode('.', $filename); $ext = $exploded_filename[count($exploded_filename) - 1]; $id = auth()->id(); $mode = 'create'; if ($id) { if ($request->has('tempPath')) { $temp_path = $request->tempPath; } else { $temp_path = $this->getTempLocalStoragePath($id, $mode); } if ($temp_path) { $ext = 'png'; if($request->has('type') && $request->type == 'short'){ $temp_filename = "000-is-uploaded-short.$ext"; } else { $temp_filename = "000-is-uploaded.$ext"; } Storage::delete("$temp_path/thumbnails/$temp_filename"); $thumbnail_local_url = Storage::putFileAs("$temp_path/thumbnails", $file, $temp_filename); $thumbnails = Storage::allFiles("$temp_path/thumbnails"); $ret = []; $i = 0; foreach ($thumbnails as $thumbnail) { $thumbnail_filename = basename($thumbnail); $thumbnail_path = asset("storage/app/$temp_path/thumbnails/$thumbnail_filename"); $ret[] = [ 'key' => Str::random(), 'id' => $thumbnail_filename, 'thumbnail_url' => $thumbnail_path, 'filename' => $thumbnail_filename, 'is_short' => str_contains($thumbnail_filename, 'is-uploaded-short') ? true : false, 'is_uploaded' => str_contains($thumbnail_filename, 'is-uploaded') ? true : false, ]; $i++; } return $ret; } } return null; } public function processVideo($id, $mode = 'update') { ProcessVideo::dispatch($id, $mode); } public function asyncStoreVideo($id) { StoreVideo::dispatch($id); } public function updateVideo($id, $mode = 'update') { $video = StudioPlusVideo::find($id); if ($video) { $video->upload_status = StudioPlusVideo::UPLOAD_STATUS_PROCESSING; $video->update(); $temp_path = $this->getTempUploadStoragePath($id); //$result_path = storage_path("app/$temp_path/result.mp4"); Storage::delete("$temp_path/result.mp4"); //$is_short = Storage::exists("$temp_path/short.dat"); Storage::copy("$temp_path/video.mp4", "$temp_path/result.mp4"); if (Storage::exists("$temp_path/result.mp4")) { Storage::put("$temp_path/processed.dat", 'Video processed.'); $this->storeVideo($id, $mode); } } } public function updateVideox($id, $mode = 'update') { $studio_plus_video = StudioPlusVideo::find($id); $temp_path = $this->getTempUploadStoragePath($id); if ($studio_plus_video) { //$studio_plus_video->upload_status = StudioPlusVideo::UPLOAD_STATUS_PROCESSING; //$studio_plus_video->update(); Log::info('Processing video '.$studio_plus_video->id); } $globalSetting = $studio_plus_video->globalSetting; /*if ($globalSetting) { $introLocalPath = storage_path('app/public/' . $globalSetting->details['intro']); $outroLocalPath = storage_path('app/public/' . $globalSetting->details['outro']); $outroOutputPath = storage_path("app/$temp_path/outro.mp4"); Storage::delete("$temp_path/outro.mp4"); VideoService::copyAndRescale($outroLocalPath, $outroOutputPath); Log::info('Copied outro'); Log::info('intro outro paths'); Log::info($introLocalPath); Log::info($outroLocalPath); } else { $introLocalPath = module_path('StudioPlus') . "/Storage/default.ts"; Log::info('no global setting'); } $introOutputPath = storage_path("app/$temp_path/intro.mp4"); Storage::delete("$temp_path/intro.mp4"); VideoService::copyAndRescale($introLocalPath, $introOutputPath); Log::info('Copied intro');*/ // Copy watermark $watermark = storage_path('app/CreatorStudio/watermark-transparent.png'); $video = storage_path("app/$temp_path/video.mp4"); //$outroOutputPath = isset($outroOutputPath) ? $outroOutputPath : $introOutputPath; $resultPath = storage_path("app/$temp_path/result.mp4"); $result_path = $resultPath; Storage::delete("$temp_path/result.mp4"); $is_short = Storage::exists("$temp_path/short.dat"); try { /*Storage::copy("$temp_path/video.mp4", "$temp_path/result.mp4"); Log::info('Copied video as result for short'); if ($is_short) { Storage::copy("$temp_path/video.mp4", "$temp_path/result.mp4"); Log::info('Copied video as result for short'); } else { Log::info('Merging videos'); $this->concat( $introOutputPath, $video, $resultPath, $outroOutputPath ); Log::info('Merged videos'); }*/ if ($is_short) { Storage::copy("$temp_path/video.mp4", "$temp_path/result.mp4"); } else { $this->watermark_service->addWatermark($studio_plus_video, $result_path); } } catch(\Exception $ex) { Log::error('Exception processing video'); Log::error($ex); } if (Storage::exists("$temp_path/result.mp4")) { Storage::put("$temp_path/processed.dat", 'Added intro and outro videos.'); StoreVideo::dispatch($id, $mode); } } public function storeVideo($id, $mode = 'update') { $video = StudioPlusVideo::find($id); if ($video) { $gcp = new GoogleCloudPlatform(); $temp_path = $this->getTempUploadStoragePath($id); $is_processed = Storage::exists("$temp_path/processed.dat"); $is_stored = Storage::exists("$temp_path/stored.dat"); if ($is_processed && ! $is_stored) { $original_video_path = storage_path("app/$temp_path/video.mp4"); $ffprobe = FFProbe::create(get_ffprobe_config()); $code_ac = $ffprobe->format($original_video_path)->get('duration'); $original_video_filename = 'O'.Carbon::now()->format('YmdHis').Str::random().'.mp4'; $original_video_object_name = $gcp->getGoogleStoragePath($video)."/$original_video_filename"; $gcp->uploadFileToGCP($original_video_path, $original_video_object_name, $video->videoType->vertical_video ? $video->title : $video->asin); $result_path = storage_path("app/$temp_path/result.mp4"); $filename = 'P'.Carbon::now()->format('YmdHis').Str::random().'.mp4'; $object_name = $gcp->getGoogleStoragePath($video)."/$filename"; $gcp->uploadFileToGCP($result_path, $object_name, $video->videoType->vertical_video ? $video->title : $video->asin); $gcp_storage_path = $gcp->getGoogleStoragePath($video); // $thumbnails = Storage::allFiles("$temp_path/thumbnails"); // $has_active_thumbnail = false; // if ($mode == 'update') { // $video->thumbnails()->where('is_uploaded',0)->delete(); // $has_active_thumbnail = $video->thumbnails()->where('is_active',1)->first(); // } // $i = 0; // foreach ($thumbnails as $thumbnail) { // $thumbnail_filename = basename($thumbnail); // $thumbnail_nfilename = Carbon::now()->format('YmdHis').Str::random().$thumbnail_filename; // $thumbnail_path = "$gcp_storage_path/thumbnails/$thumbnail_nfilename"; // $gcp->uploadFileToGCP(storage_path("app/$temp_path/thumbnails/$thumbnail_filename"), $thumbnail_path); // if ($video->thumbnail_id) { // $is_active = $video->thumbnail_id == $thumbnail_filename; // } else { // $is_active = $i === 0; // } // $video->thumbnails()->create([ // 'thumbnail_url' => "$thumbnail_path", // 'filename' => $thumbnail_nfilename, // 'is_active' => $has_active_thumbnail ? 0 : $is_active, // 'is_uploaded' => str_contains($thumbnail_filename, 'is-uploaded') ? true : false, // ]); // $i++; // } /*$total_reviews = 0; $product_details = (new AmazonProductService())->getProductDetails($video->asin); if ($product_details) { $total_reviews = $product_details['amazon_total_reviews']; }*/ $video->update([ 'original_video_url' => $original_video_object_name, 'video_url' => $object_name, 'video_duration' => round($code_ac, PHP_ROUND_HALF_UP), 'upload_status' => StudioPlusVideo::UPLOAD_STATUS_PENDING, ]); Storage::put("$temp_path/stored.dat", 'Video and thumbnails has been uploaded.'); Storage::deleteDirectory(Storage::get("$temp_path/temp_path.dat")); Storage::deleteDirectory($temp_path); return true; } else { sleep(5); StoreVideo::dispatch($id); } } return false; } public function createVideo($request, $studio_plus_channel) { $gcp = new GoogleCloudPlatform(); $id = auth()->id(); $token = $request->token; //$temp_path = $this->getTempLocalStoragePath($id, $token, 'create'); $temp_path = $request->tempPath; if ($temp_path) { if ($request->has('is_short') && $request->is_short) { Storage::put("$temp_path/short.dat", 'Short Video'); } Storage::put("$temp_path/thumbnail.dat", $request->thumbnail_id); $filename = Storage::get("$temp_path/filename.dat"); $filename = str_replace(' ', '', $filename); $exploded_filename = explode('.', $filename); $title = collect(Arr::except($exploded_filename, array_key_last($exploded_filename)))->join(' '); $video_local_url = "storage/app/$temp_path/video.mp4"; $params = $request->except('_token'); $params['original_video_url'] = $video_local_url; $params['video_url'] = $video_local_url; $video = $gcp->storeNewVideo( $studio_plus_channel, $title, $filename, $params ); // $thumbnails = Storage::allFiles("$temp_path/thumbnails"); // $i = 0; // foreach ($thumbnails as $thumbnail) { // $thumbnail_filename = basename($thumbnail); // $thumbnail_path = "app/$temp_path/thumbnails/$thumbnail_filename"; // if ($request->has('thumbnail_id')) { // $is_active = $request->thumbnail_id == $thumbnail_filename; // } else { // $is_active = $i === 0; // } // $video->thumbnails()->create([ // 'thumbnail_url' => "$thumbnail_path", // 'filename' => $thumbnail_filename, // 'is_active' => $is_active, // 'is_short' => str_contains($thumbnail_filename, 'is-uploaded-short') ? true : false, // 'is_uploaded' => str_contains($thumbnail_filename, 'is-uploaded') ? true : false, // ]); // $i++; // } Storage::put("$temp_path/temp_path.dat", $temp_path); $video_upload_path = $this->getTempUploadStoragePath($video->id); File::copyDirectory(storage_path("app/$temp_path"), storage_path("app/$video_upload_path")); if ($video) { $video_upload_temp_path = "storage/app/$video_upload_path/video.mp4"; $video->original_video_url = $video_upload_temp_path; $video->video_url = $video_upload_temp_path; $video->studio_plus_asin_id = !$video->videoType->vertical_video ? $request->studio_plus_asin_id : null; $video->save(); } return $video; } return false; } public function handleUpdateThumbnail($id, $file) { $video = StudioPlusVideo::find($id); if ($video) { $gcp = new GoogleCloudPlatform(); $filename = $file->getClientOriginalName(); $exploded_filename = explode('.', $filename); $ext = $exploded_filename[count($exploded_filename) - 1]; $nfilename = Carbon::now()->format('YmdHis').Str::random().$filename; $full_path = '/uploads/users/'.auth()->id()."/videos/{$video->id}/thumbnails/$nfilename"; $gcp->uploadFileToGCP($file, $full_path); $video->thumbnails()->update([ 'is_active' => false, 'is_uploaded' => false, ]); $thumbnail = $video->thumbnails()->create([ 'filename' => $filename, 'thumbnail_url' => $full_path, 'is_active' => true, 'is_short' => str_contains($filename, 'is-uploaded-short') ? true : false, 'is_uploaded' => true, ]); return $thumbnail; } return null; } public function getTempLocalStoragePath($id, $token = 'default', $mode = 'update') { if ($mode == 'create') { $stamp = Carbon::now()->format('YmdHis'); $uid = $stamp.Str::random(); $path = "CreatorStudio/temp/user/$id/$token/$uid"; return $path; } else { $video = StudioPlusVideo::find($id); if ($video) { return "CreatorStudio/temp/videos/$id"; } } return null; } public function getTempUploadStoragePath($id) { $video = StudioPlusVideo::find($id); if ($video) { return "CreatorStudio/temp/upload/$id"; } return null; } public function concat(string $intro, string $pathToVideo, string $pathToOutput, string $outro = ''): void { $newOutro = $outro ?: $intro; `ffmpeg -i "$intro" -i "$pathToVideo" -i "$newOutro" -filter_complex "[0:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1,setsar=1,fps=30,format=yuv420p[v0];[1:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1,setsar=1,fps=30,format=yuv420p[v1];[2:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1,setsar=1,fps=30,format=yuv420p[v2];[v0][0:a][v1][1:a][v2][2:a]concat=n=3:v=1:a=1[v][a]" -map "[v]" -map "[a]" -c:v libx264 -c:a aac -movflags +faststart "$pathToOutput"`; } public function getVideoResolution($video_path) { Log::info('Getting Video Resolution:'); Log::info($video_path); $process = new Process(['ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=width,height', '-of', 'json', $video_path]); $process->run(); if ($process->isSuccessful()) { $output = json_decode($process->getOutput(), true); Log::info('GetVideoResolution Success.'); Log::info($output); return $output['streams'][0]; } else { Log::info($process->getOutput()); Log::info('GetVideoResolution Failed.'); } return false; } public function watermarked($video_path, $watermark_path, $output_video_path) { `ffmpeg -i "$video_path" -i "$watermark_path" -filter_complex "overlay=10:10:enable='between(t\\,0\\,3)'" "$output_video_path"`; } public function getVideoDuration($video) { $ffprobe = FFProbe::create(get_ffprobe_config()); $duration = $ffprobe ->streams($video) ->videos() ->first() ->get('duration'); return $duration; } public function addWatermark($video, $watermark, $output) { $output_stage_1 = str_replace('result', 'result-1', $output); $output_stage_2 = str_replace('result', 'result-2', $output); $output_stage_3 = str_replace('result', 'result-3', $output); $this->addStartingWatermark($video, $watermark, $output_stage_1); $this->addEndingWatermark($output_stage_1, $watermark, $output_stage_2); $this->addTextWatermark($output_stage_2, 'WTI Creator Studio', $output_stage_3); } public function addStartingWatermark($video, $watermark, $output) { `ffmpeg -i "$video" -i "$watermark" -filter_complex "overlay=10:10:enable='between(t\\,0\\,3)'" "$output"`; } public function addEndingWatermark($video, $watermark, $output) { $duration = $this->getVideoDuration($video); if ($duration) { $duration = ceil($duration); $start = $duration - 3; $end = $duration; `ffmpeg -i "$video" -i "$watermark" -filter_complex "overlay=10:10:enable='between(t\\,$start\\,$end)'" "$output"`; } } public function addTextWatermark($video, $watermark, $output) { $font = storage_path('app/CreatorStudio/sample.ttf'); $font = str_replace('\\', '/', $font); Log::info($font); $font = 'D\:/Works/wticreatorstudio/storage/app/CreatorStudio/sample.ttf'; `ffmpeg -i "$video" -vf "drawtext=fontfile='$font':text='$watermark':fontcolor=white:fontsize=48:box=1:boxcolor=black@0.5:boxborderw=20:x=(w-text_w)/2:y=(h-text_h)/2:enable='between(t\\,5\\,10)'" "$output"`; } protected function isResolutionAllowed($resolution) { $allowed_resolutions = $this->video_resolutions_service->getActive(); foreach ($allowed_resolutions as $item) { if ($item->width == $resolution['width'] && $item->height == $resolution['height']) { return true; } } return false; } }