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); } }