takeone-youtube-clone/app/Http/Controllers/VideoMergeController.php
ghassan 8d27520bdb Video merge feature, default avatar, video-redirects table, misc UI tweaks
Bundled pre-existing changes on the branch:
- Video merge: controller, service, admin merge modal, and a repair
  console command; new video_redirects table (with source_folder)
  so merged/moved videos keep resolving under their old keys.
- Default avatar SVG for users without a profile picture.
- Assorted view tweaks across profile, video actions/comments/insights,
  admin layout, video-details, and the app layout.
- Small touch-ups in SuperAdminController, User model, NasSyncService.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-31 02:42:43 +03:00

99 lines
3.2 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Video;
use App\Services\VideoMergeService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class VideoMergeController extends Controller
{
public function __construct(private VideoMergeService $merger) {}
/**
* List the current user's other music videos that $target can be merged with.
*/
public function candidates(Request $request, Video $target)
{
$this->authorizeOwner($target);
if ($target->type !== 'music') {
return response()->json(['error' => 'Only music videos can be merged.'], 422);
}
$q = trim((string) $request->query('q', ''));
$query = Video::where('user_id', Auth::id())
->where('type', 'music')
->where('id', '!=', $target->id)
->orderByDesc('created_at');
if ($q !== '') {
$query->where('title', 'like', '%' . $q . '%');
}
$items = $query->limit(30)->get(['id', 'title', 'thumbnail', 'created_at'])->map(function ($v) {
return [
'id' => $v->getRouteKey(),
'title' => $v->title,
'thumbnail' => $v->thumbnail_url,
'created' => $v->created_at?->diffForHumans(),
];
});
return response()->json(['items' => $items]);
}
public function preview(Video $target, Video $source)
{
$this->authorizeOwner($target);
$this->authorizeOwner($source);
if ($target->type !== 'music' || $source->type !== 'music') {
return response()->json(['error' => 'Only music videos can be merged.'], 422);
}
if ($target->id === $source->id) {
return response()->json(['error' => 'Cannot merge a video with itself.'], 422);
}
return response()->json([
'source' => [
'id' => $source->getRouteKey(),
'title' => $source->title,
'thumb' => $source->thumbnail_url,
],
'target' => [
'id' => $target->getRouteKey(),
'title' => $target->title,
'thumb' => $target->thumbnail_url,
],
'counts' => $this->merger->preview($source, $target),
]);
}
public function merge(Request $request, Video $target, Video $source)
{
$this->authorizeOwner($target);
$this->authorizeOwner($source);
try {
$this->merger->merge($source, $target);
} catch (\Throwable $e) {
\Log::error('Video merge failed', [
'source' => $source->id,
'target' => $target->id,
'err' => $e->getMessage(),
]);
return response()->json(['error' => 'Merge failed: ' . $e->getMessage()], 500);
}
return response()->json([
'ok' => true,
'redirect' => route('videos.show', $target),
]);
}
private function authorizeOwner(Video $video): void
{
$user = Auth::user();
if (!$user) abort(401);
if ($user->id !== $video->user_id && !$user->isSuperAdmin()) abort(403);
}
}