File size: 4,325 Bytes
8baf129 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | <?php
namespace App\Http\Controllers;
use App\Models\Video;
use App\Services\VideoImporter;
use App\Services\HLS\HLSConverter;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class VideoController extends Controller
{
protected $importer;
protected $hlsConverter;
public function __construct(VideoImporter $importer, HLSConverter $hlsConverter)
{
$this->importer = $importer;
$this->hlsConverter = $hlsConverter;
}
// === IMPORT VIDEO + HLS ===
public function import(Request $request)
{
$this->validate($request, [
'import_link' => 'required|url',
'import_title' => 'nullable|string|max:150',
'enable_hls' => 'nullable|boolean',
'hls_quality' => 'nullable|array|min:1|max:3',
'hls_quality.*' => 'in:1080,720,480'
]);
try {
$result = $this->importer->import(
$request->input('import_link'),
$request->input('import_title', ''),
(bool) $request->input('enable_hls', false),
$request->input('hls_quality', ['720'])
);
return response()->json([
'status' => 'success',
'data' => $result
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => $e->getMessage()
], 500);
}
}
// === STREAM HLS (master.m3u8, playlist.m3u8, .ts, .chunk) ===
public function streamHls($folder, $file = 'master.m3u8')
{
$path = public_path("hls/{$folder}/{$file}");
if (!file_exists($path)) {
return response()->json(['error' => 'File not found'], 404);
}
$ext = pathinfo($path, PATHINFO_EXTENSION);
// Xử lý .m3u8 → rewrite URL .ts
if ($ext === 'm3u8') {
$content = file_get_contents($path);
$baseUrl = url("/api/hls/{$folder}");
// Thay thế các segment .ts, .chunk hoặc .m4s thành full URL
$content = preg_replace(
'/(^\s*)(?!#)([\w\-\.%]+\.(ts|chunk|m4s))/m',
"$1{$baseUrl}/$2",
$content
);
return response($content, 200, [
'Content-Type' => 'application/vnd.apple.mpegurl',
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, OPTIONS',
'Access-Control-Allow-Headers' => 'Range',
'Cache-Control' => 'no-cache',
]);
}
// Xử lý .ts, .chunk, .m4s
$mime = in_array($ext, ['ts', 'chunk']) ? 'video/mp2t' : 'video/mp4';
$response = new BinaryFileResponse($path);
$response->headers->set('Content-Type', $mime);
$response->headers->set('Access-Control-Allow-Origin', '*');
$response->headers->set('Accept-Ranges', 'bytes');
$response->headers->set('Cache-Control', 'no-cache');
return $response;
}
// === DANH SÁCH VIDEO ===
public function index(Request $request)
{
$perPage = $request->input('per_page', 15);
$videos = Video::orderBy('created_at', 'desc')->paginate($perPage);
return response()->json($videos);
}
public function show($id)
{
$video = Video::findOrFail($id);
return response()->json($video);
}
// === XÓA VIDEO + HLS FILES ===
public function destroy($id)
{
$video = Video::findOrFail($id);
// Xóa file gốc
if ($video->path && file_exists($video->path)) {
@unlink($video->path);
}
// Xóa thumbnail
if ($video->thumbnail && file_exists(storage_path('app/' . $video->thumbnail))) {
@unlink(storage_path('app/' . $video->thumbnail));
}
// Xóa HLS files
if ($video->hls_enabled && $video->hls_master_playlist) {
$this->hlsConverter->deleteHLSFiles($video->hls_master_playlist);
}
$video->delete();
return response()->json([
'status' => 'success',
'message' => 'Đã xóa video thành công'
]);
}
}
|