middleware('auth')->except(['index', 'show', 'publicPlaylists', 'userPlaylists']); } // List user's playlists public function index() { $user = Auth::user(); $playlists = $user->playlists()->orderBy('created_at', 'desc')->get(); return view('playlists.index', compact('playlists')); } // View a single playlist public function show(Playlist $playlist) { // Check if user can view this playlist if (! $playlist->canView(Auth::user())) { abort(404, 'Playlist not found'); } $videos = $playlist->videos()->orderBy('position')->paginate(20); return view('playlists.show', compact('playlist', 'videos')); } // Create new playlist form public function create() { return view('playlists.create'); } // Store new playlist public function store(Request $request) { $request->validate([ 'name' => 'required|string|max:100', 'description' => 'nullable|string|max:500', 'visibility' => 'nullable|in:public,private', 'thumbnail' => 'nullable|image|mimes:jpeg,png,gif,webp|max:5120', ]); $playlistData = [ 'user_id' => Auth::id(), 'name' => $request->name, 'description' => $request->description, 'visibility' => $request->visibility ?? 'private', 'is_default' => false, ]; // Create playlist first to get ID for thumbnail naming $playlist = Playlist::create($playlistData); // Handle thumbnail upload if ($request->hasFile('thumbnail')) { $file = $request->file('thumbnail'); $filename = 'playlist_'.$playlist->id.'_'.time().'.'.$file->getClientOriginalExtension(); $file->storeAs('public/thumbnails', $filename); $playlist->update(['thumbnail' => $filename]); } // Reload playlist with thumbnail $playlist->refresh(); if ($request->expectsJson() || $request->ajax()) { return response()->json([ 'success' => true, 'playlist' => [ 'id' => $playlist->id, 'name' => $playlist->name, 'visibility' => $playlist->visibility, 'thumbnail_url' => $playlist->thumbnail_url, ], ]); } return redirect()->route('playlists.show', $playlist)->with('success', 'Playlist created!'); } // Edit playlist form public function edit(Playlist $playlist) { // Check ownership if (! $playlist->canEdit(Auth::user())) { abort(403, 'You do not have permission to edit this playlist.'); } if (request()->expectsJson() || request()->ajax()) { return response()->json([ 'success' => true, 'playlist' => [ 'id' => $playlist->id, 'name' => $playlist->name, 'description' => $playlist->description, 'visibility' => $playlist->visibility, ], ]); } return view('playlists.edit', compact('playlist')); } // Update playlist public function update(Request $request, Playlist $playlist) { // Check ownership if (! $playlist->canEdit(Auth::user())) { abort(403, 'You do not have permission to edit this playlist.'); } $request->validate([ 'name' => 'required|string|max:100', 'description' => 'nullable|string|max:500', 'visibility' => 'nullable|in:public,private', 'thumbnail' => 'nullable|image|mimes:jpeg,png,gif,webp|max:5120', ]); $updateData = [ 'name' => $request->name, 'description' => $request->description, 'visibility' => $request->visibility ?? 'private', ]; // Handle thumbnail upload if ($request->hasFile('thumbnail')) { // Delete old thumbnail if exists if ($playlist->thumbnail) { $oldPath = storage_path('app/public/thumbnails/'.$playlist->thumbnail); if (file_exists($oldPath)) { unlink($oldPath); } } // Upload new thumbnail $file = $request->file('thumbnail'); $filename = 'playlist_'.$playlist->id.'_'.time().'.'.$file->getClientOriginalExtension(); $file->storeAs('public/thumbnails', $filename); $updateData['thumbnail'] = $filename; } // Handle thumbnail removal if ($request->input('remove_thumbnail') == '1') { if ($playlist->thumbnail) { $oldPath = storage_path('app/public/thumbnails/'.$playlist->thumbnail); if (file_exists($oldPath)) { unlink($oldPath); } $updateData['thumbnail'] = null; } } $playlist->update($updateData); if ($request->expectsJson() || $request->ajax()) { return response()->json([ 'success' => true, 'message' => 'Playlist updated!', 'playlist' => [ 'id' => $playlist->id, 'name' => $playlist->name, 'visibility' => $playlist->visibility, ], ]); } return redirect()->route('playlists.show', $playlist)->with('success', 'Playlist updated!'); } // Delete playlist public function destroy(Request $request, Playlist $playlist) { // Check ownership if (! $playlist->canEdit(Auth::user())) { abort(403, 'You do not have permission to delete this playlist.'); } // Don't allow deleting default playlists if ($playlist->is_default) { abort(400, 'Cannot delete default playlist.'); } $playlist->delete(); if ($request->expectsJson() || $request->ajax()) { return response()->json([ 'success' => true, 'message' => 'Playlist deleted!', ]); } return redirect()->route('playlists.index')->with('success', 'Playlist deleted!'); } // Add video to playlist public function addVideo(Request $request, Playlist $playlist) { // Check ownership if (! $playlist->canEdit(Auth::user())) { abort(403, 'You do not have permission to edit this playlist.'); } $request->validate([ 'video_id' => 'required|exists:videos,id', ]); $video = Video::findOrFail($request->video_id); // Check if video can be viewed if (! $video->canView(Auth::user())) { abort(403, 'You cannot add this video to your playlist.'); } $added = $playlist->addVideo($video); if ($request->expectsJson() || $request->ajax()) { return response()->json([ 'success' => true, 'message' => $added ? 'Video added to playlist!' : 'Video is already in playlist.', 'video_count' => $playlist->video_count, ]); } return back()->with('success', $added ? 'Video added to playlist!' : 'Video is already in playlist.'); } // Remove video from playlist public function removeVideo(Request $request, Playlist $playlist, Video $video) { // Check ownership if (! $playlist->canEdit(Auth::user())) { abort(403, 'You do not have permission to edit this playlist.'); } $removed = $playlist->removeVideo($video); if ($request->expectsJson() || $request->ajax()) { return response()->json([ 'success' => true, 'message' => 'Video removed from playlist.', 'video_count' => $playlist->video_count, ]); } return back()->with('success', 'Video removed from playlist.'); } // Reorder videos in playlist public function reorder(Request $request, Playlist $playlist) { // Check ownership if (! $playlist->canEdit(Auth::user())) { abort(403, 'You do not have permission to edit this playlist.'); } $request->validate([ 'video_ids' => 'required|array', 'video_ids.*' => 'integer|exists:videos,id', ]); $playlist->reorderVideos($request->video_ids); if ($request->expectsJson() || $request->ajax()) { return response()->json([ 'success' => true, 'message' => 'Playlist reordered!', ]); } return back()->with('success', 'Playlist reordered!'); } // Get user's playlists (for dropdown) public function userPlaylists() { // Handle unauthenticated users if (! Auth::check()) { return response()->json([ 'success' => true, 'playlists' => [], 'authenticated' => false, ]); } $user = Auth::user(); $playlists = $user->playlists()->orderBy('name')->get(); // Get video IDs for each playlist $playlistsWithVideoIds = $playlists->map(function ($p) { return [ 'id' => $p->id, 'name' => $p->name, 'description' => $p->description, 'video_count' => $p->videos()->count(), 'formatted_duration' => $p->formatted_duration, 'is_default' => $p->is_default, 'visibility' => $p->visibility, 'thumbnail_url' => $p->thumbnail_url, 'video_ids' => $p->videos()->pluck('videos.id')->toArray(), ]; }); return response()->json([ 'success' => true, 'playlists' => $playlistsWithVideoIds, 'authenticated' => true, ]); } // Quick add to Watch Later public function watchLater(Request $request, Video $video) { $watchLater = Playlist::getWatchLater(Auth::id()); $added = $watchLater->addVideo($video); if ($request->expectsJson() || $request->ajax()) { return response()->json([ 'success' => true, 'message' => $added ? 'Added to Watch Later!' : 'Already in Watch Later.', ]); } return back()->with('success', $added ? 'Added to Watch Later!' : 'Already in Watch Later.'); } // Update watch progress public function updateProgress(Request $request, Playlist $playlist, Video $video) { $request->validate([ 'seconds' => 'required|integer|min:0', ]); $playlist->updateWatchProgress($video, $request->seconds); return response()->json([ 'success' => true, ]); } // Play all videos in playlist (redirect to first video with playlist context) public function playAll(Playlist $playlist) { if (! $playlist->canView(Auth::user())) { abort(404, 'Playlist not found'); } $firstVideo = $playlist->videos()->orderBy('position')->first(); if (! $firstVideo) { return back()->with('error', 'Playlist is empty.'); } // Redirect to first video with playlist parameter return redirect()->route('videos.show', [ 'video' => $firstVideo->id, 'playlist' => $playlist->id, ]); } // Shuffle play - redirect to random video public function shuffle(Playlist $playlist) { if (! $playlist->canView(Auth::user())) { abort(404, 'Playlist not found'); } $randomVideo = $playlist->getRandomVideo(); if (! $randomVideo) { return back()->with('error', 'Playlist is empty.'); } return redirect()->route('videos.show', [ 'video' => $randomVideo->id, 'playlist' => $playlist->id, ]); } }