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' ]); } }