697 lines
26 KiB
PHP
697 lines
26 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
class ESP32CamController extends Controller
|
|
{
|
|
/**
|
|
* Upload foto dari ESP32-CAM
|
|
*/
|
|
public function upload(Request $request)
|
|
{
|
|
try {
|
|
// Direktori untuk menyimpan gambar
|
|
$uploadDir = 'esp32cam/';
|
|
|
|
// Pastikan direktori ada di storage/app/public
|
|
if (!Storage::disk('public')->exists($uploadDir)) {
|
|
Storage::disk('public')->makeDirectory($uploadDir);
|
|
}
|
|
|
|
// Buat nama file unik dengan timestamp
|
|
$filename = 'esp32cam_' . Carbon::now()->format('Ymd_His') . '.jpg';
|
|
$fullPath = $uploadDir . $filename;
|
|
|
|
// Ambil konten gambar dari request
|
|
$imageContent = $request->getContent();
|
|
|
|
if (!empty($imageContent)) {
|
|
// Simpan gambar menggunakan Laravel Storage
|
|
if (Storage::disk('public')->put($fullPath, $imageContent)) {
|
|
// Mengatur waktu file agar sesuai dengan waktu pengambilan saat ini
|
|
$now = time();
|
|
$filePath = storage_path('app/public/' . $fullPath);
|
|
@touch($filePath, $now);
|
|
|
|
// Log informasi upload menggunakan Laravel Logger
|
|
Log::info("ESP32-CAM image uploaded successfully", [
|
|
'filename' => $filename,
|
|
'path' => $fullPath,
|
|
'size' => strlen($imageContent) . ' bytes',
|
|
'time' => Carbon::now()->format('Y-m-d H:i:s')
|
|
]);
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Gambar tersimpan sebagai ' . $filename,
|
|
'path' => $fullPath,
|
|
'time' => Carbon::now()->format('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
|
|
throw new \Exception('Gagal menyimpan gambar');
|
|
}
|
|
|
|
throw new \Exception('Tidak ada data yang diterima');
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("ESP32-CAM upload error: " . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Menampilkan semua foto
|
|
*/
|
|
public function all(Request $request)
|
|
{
|
|
$uploadDir = 'esp32cam/';
|
|
$files = Storage::disk('public')->files($uploadDir);
|
|
|
|
// Urutkan file berdasarkan waktu modifikasi (terbaru lebih dulu)
|
|
usort($files, function($a, $b) {
|
|
return Storage::disk('public')->lastModified($b) - Storage::disk('public')->lastModified($a);
|
|
});
|
|
|
|
$photos = [];
|
|
foreach ($files as $file) {
|
|
$filePath = storage_path('app/public/' . $file);
|
|
$fileModTime = Storage::disk('public')->lastModified($file);
|
|
$fileName = basename($file);
|
|
$fileSize = round(Storage::disk('public')->size($file) / 1024, 2) . ' KB';
|
|
|
|
// Mendapatkan timestamp dari nama file (format nama file: esp32cam_YmdHis)
|
|
$timestampFromFilename = null;
|
|
if (preg_match('/esp32cam_(\d{8})_(\d{6})/', $fileName, $matches)) {
|
|
$dateStr = $matches[1];
|
|
$timeStr = $matches[2];
|
|
$timestampFromFilename = strtotime(
|
|
substr($dateStr, 0, 4) . '-' .
|
|
substr($dateStr, 4, 2) . '-' .
|
|
substr($dateStr, 6, 2) . ' ' .
|
|
substr($timeStr, 0, 2) . ':' .
|
|
substr($timeStr, 2, 2) . ':' .
|
|
substr($timeStr, 4, 2)
|
|
);
|
|
}
|
|
|
|
// Menggunakan timestamp dari nama file jika tersedia, jika tidak gunakan waktu modifikasi
|
|
$captureTimestamp = $timestampFromFilename ?: $fileModTime;
|
|
$captureTime = date('Y-m-d H:i:s', $captureTimestamp);
|
|
|
|
// Mendapatkan timestamp dari EXIF data jika tersedia
|
|
if (function_exists('exif_read_data')) {
|
|
try {
|
|
$exifData = @exif_read_data($filePath);
|
|
|
|
if ($exifData && isset($exifData['DateTimeOriginal'])) {
|
|
// Format EXIF datetime: YYYY:MM:DD HH:MM:SS
|
|
$exifTime = strtotime($exifData['DateTimeOriginal']);
|
|
if ($exifTime) {
|
|
$captureTimestamp = $exifTime;
|
|
$captureTime = date('Y-m-d H:i:s', $exifTime);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::warning("Error reading EXIF data: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
// Format untuk tampilan
|
|
$displayTime = date('d-m-Y H:i:s', $captureTimestamp);
|
|
|
|
$photoItem = [
|
|
'name' => $fileName,
|
|
'path' => 'storage/' . $file,
|
|
'time' => $displayTime,
|
|
'size' => $fileSize
|
|
];
|
|
|
|
$photos[] = $photoItem;
|
|
}
|
|
|
|
return view('photos', ['photos' => $photos]);
|
|
}
|
|
|
|
/**
|
|
* Mengambil foto terbaru
|
|
*/
|
|
public function latest()
|
|
{
|
|
$uploadDir = 'esp32cam/';
|
|
$files = Storage::disk('public')->files($uploadDir);
|
|
|
|
if (count($files) > 0) {
|
|
// Urutkan file berdasarkan waktu modifikasi (terbaru lebih dulu)
|
|
usort($files, function($a, $b) {
|
|
return Storage::disk('public')->lastModified($b) - Storage::disk('public')->lastModified($a);
|
|
});
|
|
|
|
$latestPhoto = $files[0];
|
|
|
|
// Get file path for EXIF reading
|
|
$filePath = storage_path('app/public/' . $latestPhoto);
|
|
$fileModTime = Storage::disk('public')->lastModified($latestPhoto);
|
|
$fileSize = round(Storage::disk('public')->size($latestPhoto) / 1024, 2) . ' KB';
|
|
$fileName = basename($latestPhoto);
|
|
|
|
// Mendapatkan timestamp dari nama file (format nama file: esp32cam_YmdHis)
|
|
$timestampFromFilename = null;
|
|
if (preg_match('/esp32cam_(\d{8})_(\d{6})/', $fileName, $matches)) {
|
|
$dateStr = $matches[1];
|
|
$timeStr = $matches[2];
|
|
$timestampFromFilename = strtotime(
|
|
substr($dateStr, 0, 4) . '-' .
|
|
substr($dateStr, 4, 2) . '-' .
|
|
substr($dateStr, 6, 2) . ' ' .
|
|
substr($timeStr, 0, 2) . ':' .
|
|
substr($timeStr, 2, 2) . ':' .
|
|
substr($timeStr, 4, 2)
|
|
);
|
|
}
|
|
|
|
// Menggunakan timestamp dari nama file jika tersedia, jika tidak gunakan waktu modifikasi
|
|
$captureTimestamp = $timestampFromFilename ?: $fileModTime;
|
|
$captureTime = date('Y-m-d H:i:s', $captureTimestamp);
|
|
|
|
// Get timestamp from EXIF data if available
|
|
if (function_exists('exif_read_data')) {
|
|
try {
|
|
$exifData = @exif_read_data($filePath);
|
|
|
|
if ($exifData && isset($exifData['DateTimeOriginal'])) {
|
|
// Format EXIF datetime: YYYY:MM:DD HH:MM:SS
|
|
$exifTime = strtotime($exifData['DateTimeOriginal']);
|
|
if ($exifTime) {
|
|
$captureTimestamp = $exifTime;
|
|
$captureTime = date('Y-m-d H:i:s', $exifTime);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::warning("Error reading EXIF data: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
// Format untuk tampilan
|
|
$displayTime = date('d-m-Y H:i:s', $captureTimestamp);
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'photo' => [
|
|
'name' => $fileName,
|
|
'path' => 'storage/' . $latestPhoto,
|
|
'time' => $displayTime,
|
|
'timestamp' => $captureTimestamp,
|
|
'size' => $fileSize
|
|
]
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Tidak ada foto yang tersedia'
|
|
], 404);
|
|
}
|
|
|
|
/**
|
|
* Hapus foto tertentu
|
|
*/
|
|
public function delete(Request $request)
|
|
{
|
|
$filename = $request->input('filename');
|
|
$uploadDir = 'esp32cam/';
|
|
|
|
if (Storage::disk('public')->exists($uploadDir . $filename)) {
|
|
Storage::disk('public')->delete($uploadDir . $filename);
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Foto berhasil dihapus'
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Foto tidak ditemukan'
|
|
], 404);
|
|
}
|
|
|
|
/**
|
|
* Membersihkan foto lama
|
|
*/
|
|
public function cleanup()
|
|
{
|
|
try {
|
|
$uploadDir = 'esp32cam/';
|
|
$files = Storage::disk('public')->files($uploadDir);
|
|
$deletedCount = 0;
|
|
|
|
// Hapus file yang lebih lama dari 7 hari, tapi simpan minimal 10 file terakhir
|
|
if (count($files) > 10) {
|
|
// Urutkan file berdasarkan waktu modifikasi (terlama lebih dulu)
|
|
usort($files, function($a, $b) {
|
|
return Storage::disk('public')->lastModified($a) - Storage::disk('public')->lastModified($b);
|
|
});
|
|
|
|
// Ambil file lama untuk dihapus (tapi sisakan 10 file terakhir)
|
|
$filesToDelete = array_slice($files, 0, count($files) - 10);
|
|
|
|
foreach ($filesToDelete as $file) {
|
|
$fileTime = Storage::disk('public')->lastModified($file);
|
|
if (time() - $fileTime > 7 * 24 * 60 * 60) { // 7 hari dalam detik
|
|
Storage::disk('public')->delete($file);
|
|
Log::info("Deleted old ESP32-CAM image: " . $file);
|
|
$deletedCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Cleanup completed. Deleted ' . $deletedCount . ' old files.',
|
|
'remaining_files' => count($files) - $deletedCount
|
|
]);
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("ESP32-CAM cleanup error: " . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch image from ESP32-CAM web server and save it
|
|
*
|
|
* @param Request $request
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function fetchFromCamera(Request $request)
|
|
{
|
|
try {
|
|
// Debug untuk melihat isi request
|
|
Log::info('ESP32CAM Debug - Request Content:', [
|
|
'all' => $request->all(),
|
|
'content' => $request->getContent(),
|
|
'content_decoded' => json_decode($request->getContent(), true),
|
|
'headers' => $request->header()
|
|
]);
|
|
|
|
// Gunakan IP kamera dari request atau default
|
|
$cameraIp = '192.168.1.19'; // Default IP
|
|
|
|
// Coba ambil dari request JSON
|
|
$requestData = json_decode($request->getContent(), true);
|
|
if (isset($requestData['camera_ip']) && !empty($requestData['camera_ip'])) {
|
|
$cameraIp = $requestData['camera_ip'];
|
|
}
|
|
|
|
Log::info('ESP32CAM Debug - Camera IP:', [
|
|
'camera_ip' => $cameraIp,
|
|
'is_empty' => empty($cameraIp)
|
|
]);
|
|
|
|
if (empty($cameraIp)) {
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'IP address ESP32-CAM diperlukan'
|
|
], 400);
|
|
}
|
|
|
|
// URL untuk mengambil gambar dari ESP32-CAM
|
|
$captureUrl = "http://{$cameraIp}/capture";
|
|
Log::info('ESP32CAM Capture URL: ' . $captureUrl);
|
|
|
|
// Ambil gambar dari ESP32-CAM dengan timeout yang lebih lama
|
|
$response = Http::timeout(30)->get($captureUrl);
|
|
|
|
if ($response->successful()) {
|
|
// Direktori untuk menyimpan gambar
|
|
$uploadDir = 'esp32cam/';
|
|
|
|
// Pastikan direktori ada di storage/app/public
|
|
if (!Storage::disk('public')->exists($uploadDir)) {
|
|
Storage::disk('public')->makeDirectory($uploadDir);
|
|
}
|
|
|
|
// Buat nama file unik dengan timestamp
|
|
$filename = 'esp32cam_' . Carbon::now()->format('Ymd_His') . '.jpg';
|
|
$fullPath = $uploadDir . $filename;
|
|
|
|
// Simpan gambar menggunakan Laravel Storage
|
|
Storage::disk('public')->put($fullPath, $response->body());
|
|
|
|
// Mengatur waktu file agar sesuai dengan waktu pengambilan saat ini
|
|
$now = time();
|
|
$filePath = storage_path('app/public/' . $fullPath);
|
|
@touch($filePath, $now);
|
|
|
|
// Log informasi
|
|
Log::info("ESP32-CAM image fetched successfully", [
|
|
'camera_ip' => $cameraIp,
|
|
'filename' => $filename,
|
|
'path' => $fullPath,
|
|
'size' => strlen($response->body()) . ' bytes',
|
|
'time' => Carbon::now()->format('Y-m-d H:i:s')
|
|
]);
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Gambar berhasil diambil dari ESP32-CAM',
|
|
'filename' => $filename,
|
|
'path' => $fullPath,
|
|
'time' => Carbon::now()->format('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
|
|
Log::error('ESP32-CAM fetch error: Failed with status ' . $response->status());
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal mengambil gambar dari ESP32-CAM. Status: ' . $response->status(),
|
|
'camera_ip' => $cameraIp
|
|
], 500);
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("ESP32-CAM fetch error: " . $e->getMessage(), [
|
|
'camera_ip' => $cameraIp ?? '192.168.1.19',
|
|
'exception' => $e
|
|
]);
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal mengambil gambar: ' . $e->getMessage(),
|
|
'camera_ip' => $cameraIp ?? '192.168.1.19'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Proxy untuk endpoint status dari ESP32-CAM
|
|
*
|
|
* @param Request $request
|
|
* @return \Illuminate\Http\Response
|
|
*/
|
|
public function proxyStatus(Request $request)
|
|
{
|
|
try {
|
|
$cameraIp = $request->query('ip');
|
|
|
|
if (empty($cameraIp)) {
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'IP address ESP32-CAM diperlukan'
|
|
], 400);
|
|
}
|
|
|
|
// Periksa apakah URL sudah mengandung http:// atau https://
|
|
if (!preg_match('/^https?:\/\//', $cameraIp)) {
|
|
$statusUrl = "http://{$cameraIp}/status";
|
|
} else {
|
|
// Gunakan URL langsung jika sudah termasuk protokol
|
|
$statusUrl = "{$cameraIp}/status";
|
|
}
|
|
|
|
Log::info('ESP32CAM Proxy Status URL: ' . $statusUrl);
|
|
|
|
$response = Http::timeout(10)->get($statusUrl);
|
|
|
|
if ($response->successful()) {
|
|
return response($response->body(), 200)
|
|
->header('Content-Type', 'application/json');
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal mengakses status ESP32-CAM. Status: ' . $response->status(),
|
|
'camera_ip' => $cameraIp
|
|
], 500);
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("ESP32-CAM proxy status error: " . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal mengakses status: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Proxy untuk stream video dari ESP32-CAM
|
|
*
|
|
* @param Request $request
|
|
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
|
*/
|
|
public function proxyStream(Request $request)
|
|
{
|
|
$cameraIp = $request->query('ip');
|
|
|
|
if (empty($cameraIp)) {
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'IP address ESP32-CAM diperlukan'
|
|
], 400);
|
|
}
|
|
|
|
// Periksa apakah URL sudah mengandung http:// atau https://
|
|
if (!preg_match('/^https?:\/\//', $cameraIp)) {
|
|
$streamUrl = "http://{$cameraIp}/stream";
|
|
} else {
|
|
// Gunakan URL langsung jika sudah termasuk protokol
|
|
$streamUrl = "{$cameraIp}/stream";
|
|
}
|
|
|
|
Log::info('ESP32CAM Proxy Stream URL: ' . $streamUrl);
|
|
|
|
// Menggunakan streaming response untuk meneruskan MJPEG stream
|
|
return response()->stream(function() use ($streamUrl) {
|
|
$ch = curl_init($streamUrl);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); // Langsung kirim output ke browser
|
|
curl_exec($ch);
|
|
curl_close($ch);
|
|
}, 200, [
|
|
'Content-Type' => 'multipart/x-mixed-replace; boundary=frame',
|
|
'Cache-Control' => 'no-cache, private',
|
|
'Connection' => 'close',
|
|
'Pragma' => 'no-cache'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Proxy untuk pengaturan kamera ESP32-CAM
|
|
*
|
|
* @param Request $request
|
|
* @return \Illuminate\Http\Response
|
|
*/
|
|
public function proxySettings(Request $request)
|
|
{
|
|
try {
|
|
$cameraIp = $request->query('ip');
|
|
$resolution = $request->query('resolution');
|
|
$quality = $request->query('quality');
|
|
|
|
if (empty($cameraIp)) {
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'IP address ESP32-CAM diperlukan'
|
|
], 400);
|
|
}
|
|
|
|
// Periksa apakah URL sudah mengandung http:// atau https://
|
|
if (!preg_match('/^https?:\/\//', $cameraIp)) {
|
|
$baseUrl = "http://{$cameraIp}";
|
|
} else {
|
|
// Gunakan URL langsung jika sudah termasuk protokol
|
|
$baseUrl = $cameraIp;
|
|
}
|
|
|
|
// Bangun URL dengan parameter yang diperlukan
|
|
$settingsUrl = "{$baseUrl}/camera-settings";
|
|
$params = [];
|
|
|
|
if (!empty($resolution)) {
|
|
$params['resolution'] = $resolution;
|
|
}
|
|
|
|
if (!empty($quality)) {
|
|
$params['quality'] = $quality;
|
|
}
|
|
|
|
if (!empty($params)) {
|
|
$settingsUrl .= '?' . http_build_query($params);
|
|
}
|
|
|
|
Log::info('ESP32CAM Proxy Settings URL: ' . $settingsUrl);
|
|
|
|
$response = Http::timeout(10)->get($settingsUrl);
|
|
|
|
if ($response->successful()) {
|
|
return response($response->body(), 200)
|
|
->header('Content-Type', 'application/json');
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal mengubah pengaturan ESP32-CAM. Status: ' . $response->status(),
|
|
'camera_ip' => $cameraIp
|
|
], 500);
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("ESP32-CAM proxy settings error: " . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal mengubah pengaturan: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Proxy untuk endpoint capture dari ESP32-CAM
|
|
*
|
|
* @param Request $request
|
|
* @return \Illuminate\Http\Response
|
|
*/
|
|
public function proxyCapture(Request $request)
|
|
{
|
|
try {
|
|
$cameraIp = $request->query('ip');
|
|
|
|
if (empty($cameraIp)) {
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'IP address ESP32-CAM diperlukan'
|
|
], 400);
|
|
}
|
|
|
|
// Periksa apakah URL sudah mengandung http:// atau https://
|
|
if (!preg_match('/^https?:\/\//', $cameraIp)) {
|
|
$captureUrl = "http://{$cameraIp}/capture";
|
|
} else {
|
|
// Gunakan URL langsung jika sudah termasuk protokol
|
|
$captureUrl = "{$cameraIp}/capture";
|
|
}
|
|
|
|
Log::info('ESP32CAM Proxy Capture URL: ' . $captureUrl);
|
|
|
|
$response = Http::timeout(15)->get($captureUrl);
|
|
|
|
if ($response->successful()) {
|
|
// Jika hanya ingin menampilkan gambar tanpa menyimpan
|
|
if ($request->query('display_only') === 'true') {
|
|
return response($response->body(), 200)
|
|
->header('Content-Type', 'image/jpeg');
|
|
}
|
|
|
|
// Jika ingin menyimpan gambar
|
|
$uploadDir = 'esp32cam/';
|
|
|
|
// Pastikan direktori ada di storage/app/public
|
|
if (!Storage::disk('public')->exists($uploadDir)) {
|
|
Storage::disk('public')->makeDirectory($uploadDir);
|
|
}
|
|
|
|
// Buat nama file unik dengan timestamp
|
|
$filename = 'esp32cam_' . Carbon::now()->format('Ymd_His') . '.jpg';
|
|
$fullPath = $uploadDir . $filename;
|
|
|
|
// Simpan gambar menggunakan Laravel Storage
|
|
Storage::disk('public')->put($fullPath, $response->body());
|
|
|
|
// Mengatur waktu file agar sesuai dengan waktu pengambilan saat ini
|
|
$now = time();
|
|
$filePath = storage_path('app/public/' . $fullPath);
|
|
@touch($filePath, $now);
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Gambar berhasil diambil dan disimpan',
|
|
'filename' => $filename,
|
|
'path' => $fullPath,
|
|
'time' => Carbon::now()->format('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal mengambil gambar dari ESP32-CAM. Status: ' . $response->status(),
|
|
'camera_ip' => $cameraIp
|
|
], 500);
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("ESP32-CAM proxy capture error: " . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal mengambil gambar: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Proxy untuk menghentikan streaming dari ESP32-CAM
|
|
*
|
|
* @param Request $request
|
|
* @return \Illuminate\Http\Response
|
|
*/
|
|
public function proxyStopStream(Request $request)
|
|
{
|
|
try {
|
|
$cameraIp = $request->query('ip');
|
|
|
|
if (empty($cameraIp)) {
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'IP address ESP32-CAM diperlukan'
|
|
], 400);
|
|
}
|
|
|
|
// Periksa apakah URL sudah mengandung http:// atau https://
|
|
if (!preg_match('/^https?:\/\//', $cameraIp)) {
|
|
$stopStreamUrl = "http://{$cameraIp}/stopstream";
|
|
} else {
|
|
// Gunakan URL langsung jika sudah termasuk protokol
|
|
$stopStreamUrl = "{$cameraIp}/stopstream";
|
|
}
|
|
|
|
Log::info('ESP32CAM Proxy Stop Stream URL: ' . $stopStreamUrl);
|
|
|
|
$response = Http::timeout(5)->get($stopStreamUrl);
|
|
|
|
if ($response->successful()) {
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Streaming dihentikan'
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal menghentikan streaming. Status: ' . $response->status(),
|
|
'camera_ip' => $cameraIp
|
|
], 500);
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("ESP32-CAM proxy stop stream error: " . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Gagal menghentikan streaming: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
}
|