Skip to content

Commit df6610b

Browse files
committed
refactor: add DB caching, split RadioController into dedicated
controllers, and clean up Welcome.vue
1 parent b77f3c9 commit df6610b

16 files changed

Lines changed: 899 additions & 558 deletions
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use App\Models\RadioStation;
7+
use App\Models\Language;
8+
use App\Models\Tag;
9+
use App\Services\RadioBrowserService;
10+
use Illuminate\Support\Facades\DB;
11+
use Illuminate\Support\Facades\Log;
12+
13+
class CacheRadioDataCommand extends Command
14+
{
15+
/**
16+
* The name and signature of the console command.
17+
*
18+
* @var string
19+
*/
20+
protected $signature = 'radio:cache';
21+
22+
/**
23+
* The console command description.
24+
*
25+
* @var string
26+
*/
27+
protected $description = 'Cache radio stations, languages, and tags from RadioBrowser API';
28+
29+
/**
30+
* Execute the console command.
31+
*/
32+
public function handle()
33+
{
34+
$this->info('Starting radio data caching...');
35+
36+
try {
37+
// Cache stations
38+
$this->cacheStations();
39+
40+
// Cache languages
41+
$this->cacheLanguages();
42+
43+
// Cache tags
44+
$this->cacheTags();
45+
46+
$this->info('Radio data caching completed successfully.');
47+
} catch (\Exception $e) {
48+
$this->error('Failed to cache radio data: ' . $e->getMessage());
49+
Log::error('CacheRadioDataCommand error: ' . $e->getMessage());
50+
return 1;
51+
}
52+
53+
return 0;
54+
}
55+
56+
private function cacheStations()
57+
{
58+
$count = RadioStation::count();
59+
$oldest = RadioStation::orderBy('last_updated', 'asc')->first();
60+
61+
$shouldFetch = $count < 500 || ($oldest && now()->diffInHours($oldest->last_updated) >= 24);
62+
63+
if (!$shouldFetch) {
64+
$this->info('Stations cache is up to date.');
65+
return;
66+
}
67+
68+
$this->info('Fetching popular stations...');
69+
$stations = app(RadioBrowserService::class)->getPopularStations(0, 500, false);
70+
71+
DB::beginTransaction();
72+
try {
73+
foreach ($stations as $station) {
74+
RadioStation::updateOrCreate(
75+
['stationuuid' => $station['stationuuid']],
76+
array_merge($station, ['last_updated' => now()->toISOString()])
77+
);
78+
}
79+
DB::commit();
80+
$this->info('Cached ' . count($stations) . ' stations.');
81+
} catch (\Exception $e) {
82+
DB::rollBack();
83+
throw $e;
84+
}
85+
}
86+
87+
private function cacheLanguages()
88+
{
89+
$this->info('Fetching languages...');
90+
$languages = app(RadioBrowserService::class)->getLanguages();
91+
92+
DB::beginTransaction();
93+
try {
94+
Language::truncate(); // Clear existing
95+
foreach ($languages as $lang) {
96+
Language::create($lang);
97+
}
98+
DB::commit();
99+
$this->info('Cached ' . count($languages) . ' languages.');
100+
} catch (\Exception $e) {
101+
DB::rollBack();
102+
throw $e;
103+
}
104+
}
105+
106+
private function cacheTags()
107+
{
108+
$this->info('Fetching tags...');
109+
$tags = app(RadioBrowserService::class)->getTags();
110+
111+
DB::beginTransaction();
112+
try {
113+
Tag::truncate(); // Clear existing
114+
foreach ($tags as $tag) {
115+
Tag::create($tag);
116+
}
117+
DB::commit();
118+
$this->info('Cached ' . count($tags) . ' tags.');
119+
} catch (\Exception $e) {
120+
DB::rollBack();
121+
throw $e;
122+
}
123+
}
124+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace App\Http\Controllers;
4+
5+
use App\Models\Language;
6+
use App\Services\RadioBrowserService;
7+
use Illuminate\Http\JsonResponse;
8+
9+
class LanguageController extends Controller
10+
{
11+
protected RadioBrowserService $radioService;
12+
13+
public function __construct(RadioBrowserService $radioService)
14+
{
15+
$this->radioService = $radioService;
16+
}
17+
18+
/**
19+
* Display a listing of the languages.
20+
*/
21+
public function index(): JsonResponse
22+
{
23+
try {
24+
$languages = Language::all();
25+
if ($languages->isNotEmpty()) {
26+
return response()->json($languages);
27+
}
28+
return response()->json($this->radioService->getLanguages());
29+
} catch (\Exception $e) {
30+
return response()->json(['error' => 'Failed to fetch languages'], 500);
31+
}
32+
}
33+
}

app/Http/Controllers/RadioController.php

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

55
use App\Services\RadioBrowserService;
6-
use Illuminate\Http\Request;
76
use Illuminate\Http\JsonResponse;
87

98
class RadioController extends Controller
@@ -28,219 +27,26 @@ public function getCountries(): JsonResponse
2827
}
2928

3029
/**
31-
* Get all languages
32-
*/
33-
public function getLanguages(): JsonResponse
34-
{
35-
try {
36-
return response()->json($this->radioService->getLanguages());
37-
} catch (\Exception $e) {
38-
return response()->json(['error' => 'Failed to fetch languages'], 500);
39-
}
40-
}
41-
42-
/**
43-
* Get all tags
44-
*/
45-
public function getTags(): JsonResponse
46-
{
47-
try {
48-
return response()->json($this->radioService->getTags());
49-
} catch (\Exception $e) {
50-
return response()->json(['error' => 'Failed to fetch tags'], 500);
51-
}
52-
}
53-
54-
/**
55-
* Get stations by country
56-
*/
57-
public function getStationsByCountry(Request $request, string $country): JsonResponse
58-
{
59-
try {
60-
$limit = $request->input('limit', 100);
61-
$offset = $request->input('offset', 0);
62-
$order = $request->input('order', 'name');
63-
$reverse = $request->boolean('reverse', false);
64-
$hideBroken = $request->boolean('hide_broken', false);
65-
66-
return response()->json($this->radioService->getStationsByCountry(
67-
$country,
68-
$limit,
69-
$offset,
70-
$order,
71-
$reverse,
72-
$hideBroken
73-
));
74-
} catch (\Exception $e) {
75-
return response()->json(['error' => 'Failed to fetch stations by country'], 500);
76-
}
77-
}
78-
79-
/**
80-
* Get stations by language
81-
*/
82-
public function getStationsByLanguage(Request $request, string $language): JsonResponse
83-
{
84-
try {
85-
// Enforce minimum length of 2 characters for language parameter
86-
if (strlen($language) < 2) {
87-
return response()->json(['error' => 'Language parameter must be at least 2 characters'], 400);
88-
}
89-
90-
$limit = $request->input('limit', 100);
91-
$offset = $request->input('offset', 0);
92-
$order = $request->input('order', 'name');
93-
$reverse = $request->boolean('reverse', false);
94-
$hideBroken = $request->boolean('hide_broken', false);
95-
96-
return response()->json($this->radioService->getStationsByLanguage(
97-
$language,
98-
$limit,
99-
$offset,
100-
$order,
101-
$reverse,
102-
$hideBroken
103-
));
104-
} catch (\Exception $e) {
105-
return response()->json(['error' => 'Failed to fetch stations by language'], 500);
106-
}
107-
}
108-
109-
/**
110-
* Get stations by tag
111-
*/
112-
public function getStationsByTag(Request $request, string $tag): JsonResponse
113-
{
114-
try {
115-
// Enforce minimum length of 2 characters for tag parameter
116-
if (strlen($tag) < 2) {
117-
return response()->json(['error' => 'Tag parameter must be at least 2 characters'], 400);
118-
}
119-
120-
$limit = $request->input('limit', 100);
121-
$offset = $request->input('offset', 0);
122-
$order = $request->input('order', 'name');
123-
$reverse = $request->boolean('reverse', false);
124-
$hideBroken = $request->boolean('hide_broken', false);
125-
126-
return response()->json($this->radioService->getStationsByTag(
127-
$tag,
128-
$limit,
129-
$offset,
130-
$order,
131-
$reverse,
132-
$hideBroken
133-
));
134-
} catch (\Exception $e) {
135-
return response()->json(['error' => 'Failed to fetch stations by tag'], 500);
136-
}
137-
}
138-
139-
/**
140-
* Search stations with multiple criteria
141-
*/
142-
public function search(Request $request): JsonResponse
143-
{
144-
try {
145-
$name = $request->input('name');
146-
if ($name && strlen(trim($name)) < 3) {
147-
return response()->json(['error' => 'Search query must be at least 3 characters'], 400);
148-
}
149-
150-
$criteria = [
151-
'name' => $name,
152-
'country' => $request->input('country'),
153-
'state' => $request->input('state'),
154-
'language' => $request->input('language'),
155-
'tag' => $request->input('tag'),
156-
'tagList' => $request->input('tagList'), // For multiple tags
157-
'codec' => $request->input('codec'),
158-
'bitrateMin' => $request->input('bitrateMin', 0),
159-
'bitrateMax' => $request->input('bitrateMax', 1000000),
160-
'geoLat' => $request->input('geoLat'),
161-
'geoLong' => $request->input('geoLong'),
162-
'geoDistance' => $request->input('geoDistance'),
163-
'hasGeoInfo' => $request->input('has_geo_info', 'both'),
164-
'hasExtendedInfo' => $request->input('has_extended_info', 'both'),
165-
'isHttps' => $request->input('is_https', 'both'),
166-
'limit' => $request->input('limit', 100),
167-
'offset' => $request->input('offset', 0),
168-
'order' => $request->input('order', 'name'),
169-
'reverse' => $request->boolean('reverse', false),
170-
'hidebroken' => $request->boolean('hide_broken', true),
171-
'nameExact' => $request->boolean('name_exact', false),
172-
'countryExact' => $request->boolean('country_exact', false),
173-
'stateExact' => $request->boolean('state_exact', false),
174-
'languageExact' => $request->boolean('language_exact', false),
175-
'tagExact' => $request->boolean('tag_exact', false),
176-
];
177-
178-
// // Remove null/empty values except for numeric defaults
179-
// $criteria = array_filter($criteria, function($value) {
180-
// return $value !== null && $value !== '' && (!is_numeric($value) || $value > 0);
181-
// });
182-
183-
return response()->json($this->radioService->searchStations($criteria));
184-
} catch (\Exception $e) {
185-
Log::error('Search error: ' . $e->getMessage());
186-
return response()->json(['error' => 'Failed to search stations: ' . $e->getMessage()], 500);
187-
}
188-
}
189-
190-
/**
191-
* Get popular stations (by clicks)
192-
*/
193-
public function getPopularStations(Request $request): JsonResponse
194-
{
195-
try {
196-
$offset = $request->input('offset', 0);
197-
$limit = $request->input('limit', 100);
198-
$hideBroken = $request->boolean('hide_broken', false);
199-
200-
return response()->json($this->radioService->getPopularStations(
201-
$offset,
202-
$limit,
203-
$hideBroken
204-
));
205-
206-
} catch (\Exception $e) {
207-
return response()->json(['error' => 'Failed to fetch popular stations'], 500);
208-
}
209-
}
210-
211-
/**
212-
* Get all stations with pagination
30+
* Get server statistics
21331
*/
214-
public function getAllStations(Request $request): JsonResponse
32+
public function getStats(): JsonResponse
21533
{
21634
try {
217-
$offset = $request->input('offset', 0);
218-
$limit = $request->input('limit', 100);
219-
$order = $request->input('order', 'name');
220-
$reverse = $request->boolean('reverse', false);
221-
$hideBroken = $request->boolean('hide_broken', false);
222-
223-
return response()->json($this->radioService->getAllStations(
224-
$offset,
225-
$limit,
226-
$order,
227-
$reverse,
228-
$hideBroken
229-
));
35+
return response()->json($this->radioService->getServerStats());
23036
} catch (\Exception $e) {
231-
return response()->json(['error' => 'Failed to fetch all stations'], 500);
37+
return response()->json(['error' => 'Failed to fetch server stats'], 500);
23238
}
23339
}
23440

23541
/**
236-
* Get server statistics
42+
* Get API endpoints
23743
*/
238-
public function getStats(): JsonResponse
44+
public function getApiEndpoints(): JsonResponse
23945
{
24046
try {
241-
return response()->json($this->radioService->getServerStats());
47+
return response()->json($this->radioService->getRadioBrowserBaseUrls());
24248
} catch (\Exception $e) {
243-
return response()->json(['error' => 'Failed to fetch server stats'], 500);
49+
return response()->json(['error' => 'Failed to fetch API endpoints'], 500);
24450
}
24551
}
24652
}

0 commit comments

Comments
 (0)