Skip to content

Commit c8650cc

Browse files
authored
Merge pull request #639 from Serph91P/feature/api-extensions
feat(api): Comprehensive API extensions
2 parents 2645a79 + 55f5f46 commit c8650cc

File tree

9 files changed

+1698
-254
lines changed

9 files changed

+1698
-254
lines changed

app/Http/Controllers/Api/M3uProxyApiController.php

Lines changed: 0 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace App\Http\Controllers\Api;
44

5-
use App\Facades\LogoFacade;
65
use App\Facades\PlaylistFacade;
76
use App\Http\Controllers\Controller;
87
use App\Models\Channel;
@@ -11,7 +10,6 @@
1110
use App\Models\StreamProfile;
1211
use App\Services\M3uProxyService;
1312
use App\Settings\GeneralSettings;
14-
use Carbon\Carbon;
1513
use Exception;
1614
use Illuminate\Http\Request;
1715
use Illuminate\Support\Facades\Log;
@@ -317,209 +315,4 @@ protected function invalidateStreamCaches(array $data): void
317315

318316
Log::info('Cache invalidated for m3u-proxy event', $data);
319317
}
320-
321-
/**
322-
* Get active streams data
323-
*
324-
* Returns the same structure as M3uProxyStreamMonitor page
325-
*/
326-
public function activeStreams(Request $request, M3uProxyService $apiService)
327-
{
328-
$apiStreams = $apiService->fetchActiveStreams();
329-
$apiClients = $apiService->fetchActiveClients();
330-
331-
// Check for connection errors
332-
if (! $apiStreams['success']) {
333-
return response()->json([
334-
'success' => false,
335-
'error' => $apiStreams['error'] ?? 'Unknown error connecting to m3u-proxy',
336-
], 500);
337-
}
338-
339-
if (! $apiClients['success']) {
340-
return response()->json([
341-
'success' => false,
342-
'error' => $apiClients['error'] ?? 'Unknown error connecting to m3u-proxy',
343-
], 500);
344-
}
345-
346-
if (empty($apiStreams['streams'])) {
347-
return response()->json([
348-
'success' => true,
349-
'streams' => [],
350-
'globalStats' => [
351-
'total_streams' => 0,
352-
'active_streams' => 0,
353-
'total_clients' => 0,
354-
'total_bandwidth_kbps' => 0,
355-
'avg_clients_per_stream' => '0.00',
356-
],
357-
'systemStats' => [],
358-
]);
359-
}
360-
361-
// Group clients by stream_id for easier lookup
362-
$clientsByStream = collect($apiClients['clients'] ?? [])
363-
->groupBy('stream_id')
364-
->toArray();
365-
366-
$streams = [];
367-
foreach ($apiStreams['streams'] as $stream) {
368-
$streamId = $stream['stream_id'];
369-
$streamClients = $clientsByStream[$streamId] ?? [];
370-
371-
// Get model information if metadata exists
372-
$model = [];
373-
if (isset($stream['metadata']['type']) && isset($stream['metadata']['id'])) {
374-
$modelType = $stream['metadata']['type'];
375-
$modelId = $stream['metadata']['id'];
376-
$title = null;
377-
$logo = null;
378-
379-
if ($modelType === 'channel') {
380-
$channel = Channel::find($modelId);
381-
if ($channel) {
382-
$title = $channel->name_custom ?? $channel->name ?? $channel->title;
383-
$logo = LogoFacade::getChannelLogoUrl($channel);
384-
}
385-
} elseif ($modelType === 'episode') {
386-
$episode = Episode::find($modelId);
387-
if ($episode) {
388-
$title = $episode->title;
389-
$logo = LogoFacade::getEpisodeLogoUrl($episode);
390-
}
391-
}
392-
393-
if ($title || $logo) {
394-
$model = [
395-
'title' => $title ?? 'N/A',
396-
'logo' => $logo,
397-
];
398-
}
399-
}
400-
401-
// Calculate uptime
402-
$startedAt = Carbon::parse($stream['created_at'], 'UTC');
403-
$uptime = $startedAt->diffForHumans(null, true);
404-
405-
// Format bytes transferred
406-
$bytesTransferred = $this->formatBytes($stream['total_bytes_served']);
407-
408-
// Calculate bandwidth (approximate based on bytes and time)
409-
$durationSeconds = $startedAt->diffInSeconds(now());
410-
$bandwidthKbps = $durationSeconds > 0
411-
? round(($stream['total_bytes_served'] * 8) / $durationSeconds / 1000, 2)
412-
: 0;
413-
414-
// Normalize clients
415-
$clients = array_map(function ($client) {
416-
$connectedAt = Carbon::parse($client['created_at'], 'UTC');
417-
$lastAccess = Carbon::parse($client['last_access'], 'UTC');
418-
419-
// Client is considered active if:
420-
// 1. is_connected is true (from API), OR
421-
// 2. last_access was within the last 30 seconds (more lenient for active streaming)
422-
$isActive = ($client['is_connected'] ?? false) || $lastAccess->diffInSeconds(now()) < 30;
423-
424-
return [
425-
'ip' => $client['ip_address'],
426-
'username' => $client['username'] ?? null,
427-
'connected_at' => $connectedAt->format('Y-m-d H:i:s'),
428-
'duration' => $connectedAt->diffForHumans(null, true),
429-
'bytes_received' => $this->formatBytes($client['bytes_served']),
430-
'bandwidth' => 'N/A', // Can calculate if needed
431-
'is_active' => $isActive,
432-
];
433-
}, $streamClients);
434-
435-
$transcoding = $stream['metadata']['transcoding'] ?? false;
436-
$transcodingFormat = null;
437-
if ($transcoding) {
438-
$profile = StreamProfile::find($stream['metadata']['profile_id'] ?? null);
439-
if ($profile) {
440-
$transcodingFormat = $profile->format === 'm3u8'
441-
? 'HLS'
442-
: strtoupper($profile->format);
443-
}
444-
}
445-
446-
$streams[] = [
447-
'stream_id' => $streamId,
448-
'source_url' => $this->truncateUrl($stream['original_url']),
449-
'current_url' => $stream['current_url'],
450-
'format' => strtoupper($stream['stream_type']),
451-
'status' => $stream['is_active'] && $stream['client_count'] > 0 ? 'active' : 'idle',
452-
'client_count' => $stream['client_count'],
453-
'bandwidth_kbps' => $bandwidthKbps,
454-
'bytes_transferred' => $bytesTransferred,
455-
'uptime' => $uptime,
456-
'started_at' => $startedAt->format('Y-m-d H:i:s'),
457-
'process_running' => $stream['is_active'] && $stream['client_count'] > 0,
458-
'model' => $model,
459-
'clients' => $clients,
460-
'has_failover' => $stream['has_failover'],
461-
'error_count' => $stream['error_count'],
462-
'segments_served' => $stream['total_segments_served'],
463-
'transcoding' => $transcoding,
464-
'transcoding_format' => $transcodingFormat,
465-
// Failover details
466-
'failover_urls' => $stream['failover_urls'] ?? [],
467-
'failover_resolver_url' => $stream['failover_resolver_url'] ?? null,
468-
'current_failover_index' => $stream['current_failover_index'] ?? 0,
469-
'failover_attempts' => $stream['failover_attempts'] ?? 0,
470-
'last_failover_time' => isset($stream['last_failover_time'])
471-
? Carbon::parse($stream['last_failover_time'], 'UTC')->format('Y-m-d H:i:s')
472-
: null,
473-
'using_failover' => ($stream['current_failover_index'] ?? 0) > 0 || ($stream['failover_attempts'] ?? 0) > 0,
474-
];
475-
}
476-
477-
// Calculate global stats
478-
$totalClients = array_sum(array_map(fn ($s) => $s['client_count'] ?? 0, $streams));
479-
$totalBandwidth = array_sum(array_map(fn ($s) => $s['bandwidth_kbps'] ?? 0, $streams));
480-
$activeStreams = count(array_filter($streams, fn ($s) => $s['status'] === 'active'));
481-
482-
$globalStats = [
483-
'total_streams' => count($streams),
484-
'active_streams' => $activeStreams,
485-
'total_clients' => $totalClients,
486-
'total_bandwidth_kbps' => round($totalBandwidth, 2),
487-
'avg_clients_per_stream' => count($streams) > 0
488-
? number_format($totalClients / count($streams), 2)
489-
: '0.00',
490-
];
491-
492-
return response()->json([
493-
'success' => true,
494-
'streams' => $streams,
495-
'globalStats' => $globalStats,
496-
'systemStats' => [], // populate if external API provides system metrics
497-
]);
498-
}
499-
500-
/**
501-
* Truncate a URL for display
502-
*/
503-
protected function truncateUrl(string $url, int $maxLength = 50): string
504-
{
505-
if (strlen($url) <= $maxLength) {
506-
return $url;
507-
}
508-
509-
return substr($url, 0, $maxLength - 3).'...';
510-
}
511-
512-
/**
513-
* Format bytes into human readable format
514-
*/
515-
protected function formatBytes(int $bytes, int $precision = 2): string
516-
{
517-
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
518-
519-
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
520-
$bytes /= 1024;
521-
}
522-
523-
return round($bytes, $precision).' '.$units[$i];
524-
}
525318
}

0 commit comments

Comments
 (0)