Files
back-meditation/app/Http/Controllers/MusicController.php
T
2026-06-06 16:38:01 +03:30

343 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\MusicPlaylist;
use App\Models\Music;
use App\Traits\HandlesImageUpload;
use App\Traits\StoresUploads;
use Illuminate\Support\Facades\Storage;
class MusicController extends Controller
{
use HandlesImageUpload, StoresUploads;
// Add this new method to your MusicController
public function getAllMusic()
{
$userId = auth()->id();
$music = Music::with(['image', 'playlists'])
->where('type', 'public')
->orWhere(function($query) use ($userId) {
$query->where('type', 'private')
->where('user_id', $userId);
})
->orderBy('created_at', 'desc')
->get();
// Return as array directly (not wrapped in 'data' object)
// to match what your old Flutter app expects
return response()->json($music);
}
public function index()
{
$userId = auth()->id();
$music = Music::with(['image', 'playlists'])
->where('type', 'public')
->orWhere(function($query) use ($userId) {
$query->where('type', 'private')
->where('user_id', $userId);
})
->orderBy('created_at', 'desc')
->get();
return response()->json([
'data' => $music,
'total' => $music->count()
]);
}
public function getMusicByPlaylist($playlistId)
{
$playlist = MusicPlaylist::findOrFail($playlistId);
$music = $playlist->musics()
->where('music.is_active', true)
->with(['image', 'tags'])
->orderBy('music_playlist.order')
->get();
return response()->json([
'playlist' => $playlist->load('image'),
'musics' => $music
]);
}
public function addToPlaylist(Request $request, $musicId)
{
$music = Music::where('id', $musicId)
->where(function($q) {
$q->where('type', 'public')
->orWhere('user_id', auth()->id());
})
->firstOrFail();
$data = $request->validate([
'playlist_id' => 'required|exists:music_playlists,id',
'order' => 'nullable|integer',
]);
$order = $data['order'] ?? $this->getNextOrderInPlaylist($data['playlist_id']);
// Add (or update its order) without removing the music from other playlists.
$music->playlists()->syncWithoutDetaching([
$data['playlist_id'] => ['order' => $order],
]);
return response()->json([
'message' => 'Music added to playlist successfully',
'music' => $music->load(['image', 'playlists'])
]);
}
public function removeFromPlaylist(Request $request, $musicId)
{
$music = Music::where('id', $musicId)
->where('user_id', auth()->id())
->firstOrFail();
$data = $request->validate([
'playlist_id' => 'required|exists:music_playlists,id',
]);
$music->playlists()->detach($data['playlist_id']);
return response()->json(['message' => 'Music removed from playlist']);
}
public function updateOrder(Request $request)
{
$data = $request->validate([
'playlist_id' => 'required|exists:music_playlists,id',
'musics' => 'required|array',
'musics.*.id' => 'required|exists:music,id',
'musics.*.order' => 'required|integer',
]);
$playlist = MusicPlaylist::findOrFail($data['playlist_id']);
foreach ($data['musics'] as $item) {
$playlist->musics()->updateExistingPivot($item['id'], [
'order' => $item['order'],
]);
}
return response()->json(['message' => 'Order updated successfully']);
}
// ✅ Upload music
public function store(Request $request)
{
try {
$data = $request->validate([
'title' => 'required|string|max:255',
'artist' => 'nullable|string|max:255',
'file' => 'required|file|extensions:mp3,wav,ogg,flac,aac,m4a,opus,mpga,wma|max:20971520',
'type' => 'nullable|in:public,private',
'image_id' => 'nullable|exists:images,id',
'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192',
'playlist_id' => 'nullable|exists:music_playlists,id', // single (backward compatible)
'playlist_ids' => 'nullable|array', // multiple
'playlist_ids.*' => 'integer|exists:music_playlists,id',
'duration' => 'nullable|integer|min:1', // validates mm:ss or hh:mm:ss
]);
// Handle file upload
if (!$request->hasFile('file')) {
return response()->json([
'message' => 'No file was uploaded'
], 400);
}
$file = $request->file('file');
// Validate file size
if ($file->getSize() > 20971520) {
return response()->json([
'message' => 'File size exceeds 10MB limit'
], 422);
}
$path = $this->storeUpload($file, 'music');
if (!$path) {
return response()->json([
'message' => 'Failed to store the file'
], 500);
}
// An uploaded image file takes precedence over a provided image_id.
$imageId = $this->uploadedImageId($request) ?? ($data['image_id'] ?? null);
$music = Music::create([
'user_id' => auth()->id(),
'title' => $data['title'],
'artist' => $data['artist'] ?? null,
'file_path' => $path,
'type' => $data['type'] ?? 'private',
'image_id' => $imageId,
'duration' => $data['duration'] ?? null, // Store as string
'is_active' => true,
]);
// Merge single + multiple playlist inputs into a unique list.
$playlistIds = collect($data['playlist_ids'] ?? [])
->push($data['playlist_id'] ?? null)
->filter()
->unique()
->values();
foreach ($playlistIds as $playlistId) {
$music->playlists()->syncWithoutDetaching([
$playlistId => ['order' => $this->getNextOrderInPlaylist($playlistId)],
]);
}
return response()->json([
'message' => 'Music uploaded successfully',
'music' => $music->load(['image', 'playlists', 'tags']),
'url' => asset('storage/' . $path),
], 201);
} catch (\Illuminate\Validation\ValidationException $e) {
return response()->json([
'message' => 'Validation failed',
'errors' => $e->errors()
], 422);
} catch (\Exception $e) {
return response()->json([
'message' => 'An error occurred while uploading the music',
'error' => $e->getMessage()
], 500);
}
}
// Helper method to get next order number in playlist
private function getNextOrderInPlaylist($playlistId)
{
if (!$playlistId) {
return 0;
}
$maxOrder = \DB::table('music_playlist')
->where('playlist_id', $playlistId)
->max('order');
return ($maxOrder ?? -1) + 1;
}
// ✅ Update music with string duration
public function update(Request $request, $id)
{
try {
$music = Music::where('id', $id)->where('user_id', auth()->id())->firstOrFail();
$data = $request->validate([
'title' => 'nullable|string|max:255',
'artist' => 'nullable|string|max:255',
'type' => 'nullable|in:public,private',
'file' => 'nullable|file|extensions:mp3,wav,ogg,flac,aac,m4a,opus,mpga,wma|max:20971520',
'image_id' => 'nullable|exists:images,id',
'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192',
'duration' => 'nullable|integer|min:1',
'is_active' => 'nullable|boolean',
]);
if ($request->hasFile('file')) {
// Delete old file
Storage::disk('public')->delete($music->file_path);
$path = $this->storeUpload($request->file('file'), 'music');
$music->file_path = $path;
}
// Update only provided fields (drop the raw file input from mass-assign).
$music->fill(collect($data)->except('image')->toArray());
// An uploaded image file takes precedence over a provided image_id.
$uploadedImageId = $this->uploadedImageId($request);
if ($uploadedImageId !== null) {
$music->image_id = $uploadedImageId;
}
$music->save();
return response()->json([
'message' => 'Music updated successfully',
'music' => $music->load(['image', 'playlists', 'tags']),
'url' => asset('storage/' . $music->file_path),
]);
} catch (\Exception $e) {
return response()->json([
'message' => 'An error occurred while updating the music',
'error' => $e->getMessage()
], 500);
}
}
// ✅ Get all public + user private music
public function show($id)
{
$userId = auth()->id();
$music = Music::with([
'image',
'playlists',
'tags',
'comments' => function($query) {
$query->with('user')->latest()->limit(10);
},
'ratings'
])
->where(function($query) use ($userId) {
$query->where('type', 'public')
->orWhere(function($q) use ($userId) {
$q->where('type', 'private')
->where('user_id', $userId);
});
})
->findOrFail($id);
// Get user's specific comment
$userComment = $music->userComment();
// Get paginated comments for the response
$comments = $music->comments()
->with('user')
->latest()
->paginate(15);
return response()->json([
'music' => $music,
'statistics' => [
'average_rating' => $music->average_rating,
'total_ratings' => $music->ratings_count,
'total_comments' => $music->comments_count,
'rating_distribution' => $music->rating_distribution,
],
'user_interaction' => [
'has_rated' => $music->has_user_rated,
'user_rating' => $music->user_rating,
'has_commented' => $music->has_user_commented,
'user_comment' => $music->user_comment,
'user_comment_id' => $music->user_comment_id,
],
'comments' => $comments,
]);
}
// ✅ Delete music
public function destroy($id)
{
$music = Music::where('id', $id)->where('user_id', auth()->id())->firstOrFail();
Storage::disk('public')->delete($music->file_path);
$music->delete();
return response()->json(['message' => 'Music deleted successfully']);
}
}