diff --git a/app/Http/Controllers/MusicCategoryController.php b/app/Http/Controllers/MusicCategoryController.php
index 9bfe53f..f914e03 100644
--- a/app/Http/Controllers/MusicCategoryController.php
+++ b/app/Http/Controllers/MusicCategoryController.php
@@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Models\MusicPlaylist;
+use App\Models\MusicCategory;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
@@ -18,22 +19,63 @@ public function index()
public function store(Request $request)
{
- $data = $request->validate([
- 'name' => 'required|string|max:255',
- 'description' => 'nullable|string',
- 'image_id' => 'nullable|exists:images,id',
- 'order' => 'nullable|integer',
- 'is_active' => 'nullable|boolean',
- ]);
-
- $data['slug'] = Str::slug($data['name']);
-
- $category = MusicCategory::create($data);
-
- return response()->json([
- 'message' => 'Category created successfully',
- 'category' => $category->load('image')
- ], 201);
+ try {
+ $data = $request->validate([
+ 'name' => 'required|string|max:255',
+ 'description' => 'nullable|string',
+ 'image_id' => 'nullable|exists:images,id',
+ 'order' => 'nullable|integer',
+ 'is_active' => 'nullable|boolean',
+ ]);
+
+ // Check for duplicate name
+ $slug = Str::slug($data['name']);
+ $existingCategory = MusicCategory::where('slug', $slug)->first();
+
+ if ($existingCategory) {
+ return response()->json([
+ 'message' => 'A category with this name already exists',
+ 'errors' => [
+ 'name' => ['The category name "' . $data['name'] . '" is already taken. Please use a different name.']
+ ]
+ ], 422);
+ }
+
+ // Optional: Check for duplicate name with case-insensitive comparison
+ $existingName = MusicCategory::whereRaw('LOWER(name) = ?', [strtolower($data['name'])])->first();
+ if ($existingName) {
+ return response()->json([
+ 'message' => 'A category with a similar name already exists',
+ 'errors' => [
+ 'name' => ['Category "' . $existingName->name . '" already exists. Please use a different name.']
+ ]
+ ], 422);
+ }
+
+ $data['slug'] = $slug;
+ $category = MusicCategory::create($data);
+
+ return response()->json([
+ 'message' => 'Category created successfully',
+ 'category' => $category->load('image')
+ ], 201);
+
+ } catch (\Illuminate\Database\QueryException $e) {
+ // Handle database duplicate entry error (if unique constraint exists)
+ if ($e->errorInfo[1] == 1062) { // MySQL duplicate entry error code
+ return response()->json([
+ 'message' => 'A category with this name already exists',
+ 'errors' => [
+ 'name' => ['The category name must be unique.']
+ ]
+ ], 422);
+ }
+
+ return response()->json([
+ 'message' => 'An error occurred while creating the category',
+ 'error' => $e->getMessage()
+ ], 500);
+ }
}
public function show($id)
diff --git a/app/Http/Controllers/MusicController.php b/app/Http/Controllers/MusicController.php
index 2a1bbe7..d880ff4 100644
--- a/app/Http/Controllers/MusicController.php
+++ b/app/Http/Controllers/MusicController.php
@@ -8,7 +8,28 @@
use Illuminate\Support\Facades\Storage;
class MusicController extends Controller
-{ public function getMusicByPlaylist($playlistId)
+{
+
+ public function index()
+ {
+ $userId = auth()->id();
+
+ $music = Music::with(['image', 'playlist'])
+ ->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);
@@ -79,83 +100,146 @@ public function updateOrder(Request $request)
// ✅ Upload music
public function store(Request $request)
{
- $data = $request->validate([
- 'title' => 'required|string|max:255',
- 'artist' => 'nullable|string|max:255',
- 'file' => 'required|mimes:mp3,wav,ogg|max:10240', // max 10MB
- 'type' => 'nullable|in:public,private',
- 'image_id' => 'nullable|exists:images,id',
- ]);
+ try {
+ $data = $request->validate([
+ 'title' => 'required|string|max:255',
+ 'artist' => 'nullable|string|max:255',
+ 'file' => 'required|mimes:mp3,wav,ogg|max:20971520',
+ 'type' => 'nullable|in:public,private',
+ 'image_id' => 'nullable|exists:images,id',
+ 'playlist_id' => 'nullable|exists:music_playlists,id',
+ 'duration' => 'nullable|string|regex:/^(?:\d+:)?[0-5]?\d:[0-5]\d$/', // validates mm:ss or hh:mm:ss
+ ]);
- $path = $request->file('file')->store('music', 'public');
+ // Handle file upload
+ if (!$request->hasFile('file')) {
+ return response()->json([
+ 'message' => 'No file was uploaded'
+ ], 400);
+ }
- $music = Music::create([
- 'user_id' => auth()->id(),
- 'title' => $data['title'],
- 'artist' => $data['artist'] ?? null,
- 'file_path' => $path,
- 'type' => $data['type'] ?? 'private',
- 'image_id' => $data['image_id'] ?? null,
- ]);
+ $file = $request->file('file');
+
+ // Validate file size
+ if ($file->getSize() > 20971520) {
+ return response()->json([
+ 'message' => 'File size exceeds 10MB limit'
+ ], 422);
+ }
- return response()->json([
- 'message' => 'Music uploaded successfully',
- 'music' => $music->load('image'),
- 'url' => asset('storage/' . $path),
- ]);
+ $path = $file->store('music', 'public');
+
+ if (!$path) {
+ return response()->json([
+ 'message' => 'Failed to store the file'
+ ], 500);
+ }
+
+ $music = Music::create([
+ 'user_id' => auth()->id(),
+ 'title' => $data['title'],
+ 'artist' => $data['artist'] ?? null,
+ 'file_path' => $path,
+ 'type' => $data['type'] ?? 'private',
+ 'image_id' => $data['image_id'] ?? null,
+ 'playlist_id' => $data['playlist_id'] ?? null,
+ 'duration' => $data['duration'] ?? null, // Store as string
+ 'order' => $this->getNextOrderInPlaylist($data['playlist_id'] ?? null),
+ 'is_active' => true,
+ ]);
+
+ return response()->json([
+ 'message' => 'Music uploaded successfully',
+ 'music' => $music->load(['image', 'playlist', '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);
+ }
}
- // ✅ Get all public + user private music
- public function all()
+ // Helper method to get next order number in playlist
+ private function getNextOrderInPlaylist($playlistId)
{
- $userId = auth()->id();
-
- $music = Music::where('type', 'public')
- ->orWhere(function($query) use ($userId) {
- $query->where('type', 'private')->where('user_id', $userId);
- })
- ->get();
-
- $music = Music::with('image') // eager load image
- ->where('type', 'public')
- ->orWhere(function($query) use ($userId) {
- $query->where('type', 'private')->where('user_id', $userId);
- })
- ->get();
-
- return response()->json($music);
+ if (!$playlistId) {
+ return 0;
+ }
+
+ $maxOrder = Music::where('playlist_id', $playlistId)->max('order');
+ return ($maxOrder ?? -1) + 1;
}
- // ✅ Update music
+ // ✅ Update music with string duration
public function update(Request $request, $id)
{
- $music = Music::where('id', $id)->where('user_id', auth()->id())->firstOrFail();
+ 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|mimes:mp3,wav,ogg|max:10240',
- 'image_id' => 'nullable|exists:images,id',
- ]);
+ $data = $request->validate([
+ 'title' => 'nullable|string|max:255',
+ 'artist' => 'nullable|string|max:255',
+ 'type' => 'nullable|in:public,private',
+ 'file' => 'nullable|mimes:mp3,wav,ogg|max:10240',
+ 'image_id' => 'nullable|exists:images,id',
+ 'playlist_id' => 'nullable|exists:music_playlists,id',
+ 'duration' => 'nullable|string|regex:/^(?:\d+:)?[0-5]?\d:[0-5]\d$/',
+ 'order' => 'nullable|integer',
+ 'is_active' => 'nullable|boolean',
+ ]);
- if ($request->hasFile('file')) {
- Storage::disk('public')->delete($music->file_path);
- $path = $request->file('file')->store('music', 'public');
- $music->file_path = $path;
+ if ($request->hasFile('file')) {
+ // Delete old file
+ Storage::disk('public')->delete($music->file_path);
+ $path = $request->file('file')->store('music', 'public');
+ $music->file_path = $path;
+ }
+
+ // Update only provided fields
+ $music->fill($data);
+ $music->save();
+
+ return response()->json([
+ 'message' => 'Music updated successfully',
+ 'music' => $music->load(['image', 'playlist', '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);
}
-
- $music->fill($data);
- $music->save();
-
-
- return response()->json([
- 'message' => 'Music updated successfully',
- 'music' => $music->load('image'),
- 'url' => asset('storage/' . $music->file_path),
- ]);
}
+
+ // ✅ Get all public + user private music
+ public function show($id)
+{
+ $userId = auth()->id();
+
+ $music = Music::with(['image', 'playlist', 'tags'])
+ ->where(function($query) use ($userId) {
+ $query->where('type', 'public')
+ ->orWhere(function($q) use ($userId) {
+ $q->where('type', 'private')
+ ->where('user_id', $userId);
+ });
+ })
+ ->findOrFail($id);
+
+ return response()->json($music);
+}
+
// ✅ Delete music
public function destroy($id)
{
diff --git a/app/Models/Music.php b/app/Models/Music.php
index 11a07ca..b372274 100644
--- a/app/Models/Music.php
+++ b/app/Models/Music.php
@@ -4,6 +4,9 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this
+use Illuminate\Database\Eloquent\Relations\BelongsToMany; // ← Add this
+use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this
class Music extends Model
{
use HasFactory;
@@ -19,6 +22,9 @@ class Music extends Model
'order' => 'integer',
'is_active' => 'boolean',
];
+ protected $attributes = [
+ 'type' => 'public', // Default value
+];
// Relation to user (optional)
public function user()
{
@@ -30,7 +36,7 @@ public function playlist(): BelongsTo
}
public function tags(): BelongsToMany
{
- return $this->belongsToMany(Tag::class, 'music_tag');
+ return $this->belongsToMany(Tag::class, 'music_tags');
}
protected $appends = ['url', 'image_url'];
diff --git a/app/Models/MusicCategory.php b/app/Models/MusicCategory.php
index 3e76707..aefa7ba 100644
--- a/app/Models/MusicCategory.php
+++ b/app/Models/MusicCategory.php
@@ -3,7 +3,8 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
-
+use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this
+use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this
class MusicCategory extends Model
{
protected $table = 'music_categories';
diff --git a/app/Models/MusicPlaylist.php b/app/Models/MusicPlaylist.php
index 5473680..f2491be 100644
--- a/app/Models/MusicPlaylist.php
+++ b/app/Models/MusicPlaylist.php
@@ -3,7 +3,8 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
-
+use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this
+use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this
class MusicPlaylist extends Model
{
protected $table = 'music_playlists';
diff --git a/app/Models/Tag.php b/app/Models/Tag.php
index f882800..002b395 100644
--- a/app/Models/Tag.php
+++ b/app/Models/Tag.php
@@ -3,7 +3,8 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
-
+use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this
+use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this
class Tag extends Model
{
protected $fillable = ['name'];
diff --git a/public/.htaccess b/public/.htaccess
index b574a59..8291745 100644
--- a/public/.htaccess
+++ b/public/.htaccess
@@ -1,3 +1,18 @@
+
+
+ php_value post_max_size 128M
+ php_value upload_max_filesize 128M
+ php_value max_execution_time 300
+ php_value max_input_time 300
+
+
+
+ php_value post_max_size 128M
+ php_value upload_max_filesize 128M
+ php_value max_execution_time 300
+ php_value max_input_time 300
+
+
Options -MultiViews -Indexes
diff --git a/public/phpinfo.php b/public/phpinfo.php
new file mode 100644
index 0000000..cf60860
--- /dev/null
+++ b/public/phpinfo.php
@@ -0,0 +1,3 @@
+
diff --git a/routes/api.php b/routes/api.php
index af59e18..eeeee92 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -15,6 +15,8 @@
use App\Http\Controllers\ImageController;
use App\Http\Controllers\MusicController;
use App\Http\Controllers\MediaController;
+use App\Http\Controllers\MusicCategoryController;
+use App\Http\Controllers\MusicPlaylistController;
Route::get('/test-hash', function() {
$plain = 'amnk1380';
@@ -120,10 +122,10 @@
/// music
- Route::post('/music', [MusicController::class, 'store']);
- Route::get('/music/all', [MusicController::class, 'all']);
- Route::put('/music/{id}', [MusicController::class, 'update']);
- Route::delete('/music/{id}', [MusicController::class, 'destroy']);
+ // Route::post('/music', [MusicController::class, 'store']);
+ // Route::get('/music/all', [MusicController::class, 'all']);
+ // Route::put('/music/{id}', [MusicController::class, 'update']);
+ // Route::delete('/music/{id}', [MusicController::class, 'destroy']);
Route::get('/slider', [SliderController::class, 'index']); // list all sliders