Skip to content

Commit dfcfc45

Browse files
committed
feat: lyrics api and updating service + tests and factories
1 parent de2aed7 commit dfcfc45

13 files changed

Lines changed: 420 additions & 18 deletions

app/Http/Controllers/Api/V1/ExternalMetadataController.php

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,21 @@
44

55
use App\Http\Controllers\Controller;
66
use App\Models\Metadata;
7+
use App\Services\External\LrcLibService;
78
use Illuminate\Http\Request;
8-
use Illuminate\Support\Facades\Http;
99

1010
class ExternalMetadataController extends Controller {
11+
public function __construct(protected LrcLibService $lrcLibService) {}
12+
1113
public function importLyrics(Request $request, $id) {
1214
$metadata = Metadata::FindOrFail($id);
13-
$data = [
14-
'artist_name' => explode(' - ', $metadata->description)[0],
15-
'track_name' => $metadata->title,
16-
// 'album_name' => $request->query('album_name'),
17-
'duration' => $metadata->duration,
18-
];
19-
$response = Http::get('https://lrclib.net/api/get', $data);
2015

21-
return response()->json(['lrclib' => $response->json(), 'payload' => $data]);
16+
return response()->json($this->lrcLibService->importLyrics($metadata));
2217
}
2318

2419
public function searchLyrics(Request $request, $id) {
2520
$metadata = Metadata::FindOrFail($id);
26-
$data = [
27-
'artist_name' => explode(' - ', $metadata->description)[0],
28-
'track_name' => $metadata->title,
29-
// 'album_name' => $request->query('album_name'),
30-
// 'duration' => $metadata->duration,
31-
];
32-
// $response = Http::get('https://lrclib.net/api/get', $data);
33-
$response = Http::get('https://lrclib.net/api/search?q=' . implode('+', $data));
3421

35-
return response()->json(['lrclib' => $response->json(), 'payload' => $data]);
22+
return response()->json($this->lrcLibService->searchLyrics($metadata, $request));
3623
}
3724
}

app/Http/Controllers/Api/V1/MetadataController.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Http\Controllers\Api\V1;
44

55
use App\Http\Controllers\Controller;
6+
use App\Http\Requests\LyricsUpdateRequest;
67
use App\Http\Requests\MetadataStoreRequest;
78
use App\Http\Requests\MetadataUpdateRequest;
89
use App\Http\Resources\MetadataResource;
@@ -72,6 +73,9 @@ public function store(MetadataStoreRequest $request) {
7273
public function update(MetadataUpdateRequest $request, Metadata $metadata) {
7374
try {
7475
$validated = $request->validated();
76+
77+
if (empty($metadata->video)) throw new \Exception('Media does not exist');
78+
7579
$validated['editor_id'] = Auth::id();
7680
$metadata->update($validated);
7781

@@ -82,4 +86,21 @@ public function update(MetadataUpdateRequest $request, Metadata $metadata) {
8286
return $this->error($request, 'Unable to edit video metadata. Error: ' . $th->getMessage(), 500);
8387
}
8488
}
89+
90+
public function updateLyrics(LyricsUpdateRequest $request, Metadata $metadata) {
91+
try {
92+
$validated = $request->validated();
93+
94+
if (empty($metadata->video)) throw new \Exception('Song does not exist');
95+
96+
unset($validated['track']);
97+
98+
$validated['editor_id'] = Auth::id();
99+
$metadata->update($validated);
100+
101+
return response()->json(new VideoResource($metadata->video), 200);
102+
} catch (\Throwable $th) {
103+
return $this->error($request, 'Unable to edit song. Error: ' . $th->getMessage(), 500);
104+
}
105+
}
85106
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace App\Http\Requests;
4+
5+
use Illuminate\Foundation\Http\FormRequest;
6+
use Illuminate\Support\Facades\Auth;
7+
8+
class LyricsUpdateRequest extends FormRequest {
9+
/**
10+
* Determine if the user is authorized to make this request.
11+
*/
12+
public function authorize(): bool {
13+
return Auth::check();
14+
}
15+
16+
/**
17+
* Get the validation rules that apply to the request.
18+
*
19+
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
20+
*/
21+
public function rules(): array {
22+
return [
23+
'track' => 'required|max:255',
24+
'artist' => 'nullable|max:255',
25+
'album' => 'nullable|max:255',
26+
'lyrics' => 'nullable',
27+
];
28+
}
29+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace App\Services\External;
4+
5+
use App\Models\Metadata;
6+
use Illuminate\Http\Request;
7+
use Illuminate\Support\Facades\Http;
8+
9+
class LrcLibService {
10+
public function importLyrics(Metadata $metadata): array {
11+
$query = array_filter([
12+
'track_name' => $metadata->title,
13+
'artist_name' => explode(' - ', $metadata->description)[0],
14+
'album_name' => $metadata->album,
15+
'duration' => $metadata->duration,
16+
], fn ($value) => $value !== null && $value !== '');
17+
18+
$response = Http::get('https://lrclib.net/api/get', $query);
19+
20+
return ['lrclib' => $response->json(), 'payload' => $query];
21+
}
22+
23+
public function searchLyrics(Metadata $metadata, Request $request): array {
24+
$query = array_filter([
25+
'track_name' => $request->query('track') ?? $metadata->title,
26+
'artist_name' => $request->query('artist') ?? explode(' - ', $metadata->description)[0],
27+
'album_name' => $request->query('album'),
28+
], fn ($value) => $value !== null && $value !== '');
29+
30+
$response = Http::get('https://lrclib.net/api/search', $query);
31+
32+
return ['lrclib' => $response->json(), 'payload' => $query];
33+
}
34+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use Illuminate\Database\Eloquent\Factories\Factory;
6+
7+
/**
8+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Category>
9+
*/
10+
class CategoryFactory extends Factory {
11+
/**
12+
* Define the model's default state.
13+
*
14+
* @return array<string, mixed>
15+
*/
16+
public function definition(): array {
17+
return [
18+
'name' => $this->faker->word(),
19+
'media_content' => false
20+
];
21+
}
22+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use App\Models\Category;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
8+
/**
9+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Folder>
10+
*/
11+
class FolderFactory extends Factory {
12+
/**
13+
* Define the model's default state.
14+
*
15+
* @return array<string, mixed>
16+
*/
17+
public function definition(): array {
18+
return [
19+
'category_id' => Category::factory(),
20+
'name' => $this->faker->sentence(2),
21+
'path' => $this->faker->unique()->word(), // or a real-looking folder path
22+
];
23+
}
24+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use App\Models\Video;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
8+
/**
9+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Metadata>
10+
*/
11+
class MetadataFactory extends Factory {
12+
/**
13+
* Define the model's default state.
14+
*
15+
* @return array<string, mixed>
16+
*/
17+
public function definition(): array {
18+
// $video = Video::factory()->create();
19+
return [
20+
'composite_id' => "{$this->faker->word}/{$this->faker->word}",
21+
// 'video_id' => $video->id,
22+
'title' => $this->faker->sentence(3),
23+
'description' => 'Fake Artist - something',
24+
'album' => $this->faker->word,
25+
'duration' => $this->faker->numberBetween(60, 500),
26+
];
27+
}
28+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use Illuminate\Database\Eloquent\Factories\Factory;
6+
7+
/**
8+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Series>
9+
*/
10+
class SeriesFactory extends Factory
11+
{
12+
/**
13+
* Define the model's default state.
14+
*
15+
* @return array<string, mixed>
16+
*/
17+
public function definition(): array
18+
{
19+
return [
20+
//
21+
];
22+
}
23+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use App\Models\Folder;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
8+
/**
9+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Video>
10+
*/
11+
class VideoFactory extends Factory {
12+
/**
13+
* Define the model's default state.
14+
*
15+
* @return array<string, mixed>
16+
*/
17+
public function definition(): array {
18+
return [
19+
'folder_id' => Folder::factory(),
20+
'name' => $this->faker->sentence(3),
21+
'path' => $this->faker->unique()->filePath(),
22+
'date' => $this->faker->date()
23+
];
24+
}
25+
}

routes/api.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
Route::resource('/series', SeriesController::class)->only(['index', 'store', 'update']);
4343
Route::resource('/tags', TagController::class)->only(['index', 'store']);
4444

45+
Route::patch('/metadata/{metadata}/lyrics', [MetadataController::class, 'updateLyrics']);
46+
4547
// Users
4648
Route::resource('/profiles', ProfileController::class)->only(['show', 'store', 'update']);
4749
Route::resource('/records', RecordController::class)->only(['index', 'store', 'destroy']);

0 commit comments

Comments
 (0)