169 lines
5.6 KiB
PHP
169 lines
5.6 KiB
PHP
#!/usr/bin/env php
|
|
<?php
|
|
/**
|
|
* Sync foto wajah dari folder (hasil download Google Drive) ke writable/faces/{student_id}.jpg
|
|
* Pemakaian: php sync_face_photos.php --source=C:\path\to\folder
|
|
* Konvensi: nama file mengandung NISN (contoh: 1234567890.jpg atau 01. 1234567890.jpg)
|
|
* Lihat: backend/docs/SYNC_FOTO_WAJAH.md
|
|
*/
|
|
|
|
$options = getopt('', ['source:']);
|
|
$source = $options['source'] ?? null;
|
|
if (empty($source) || ! is_dir($source)) {
|
|
echo "Pemakaian: php sync_face_photos.php --source=/path/to/folder\n";
|
|
echo "Folder harus berisi subfolder (XII-1, XII-2, dll) dengan file gambar bernama NISN.jpg\n";
|
|
exit(1);
|
|
}
|
|
|
|
$backendRoot = dirname(__DIR__);
|
|
$envFile = $backendRoot . DIRECTORY_SEPARATOR . '.env';
|
|
if (! is_file($envFile)) {
|
|
$envFile = $backendRoot . DIRECTORY_SEPARATOR . 'env';
|
|
}
|
|
$env = [];
|
|
if (is_file($envFile)) {
|
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || strpos($line, '#') === 0) {
|
|
continue;
|
|
}
|
|
if (strpos($line, '=') !== false) {
|
|
[$key, $val] = explode('=', $line, 2);
|
|
$key = trim($key);
|
|
$val = trim($val, " \t\"'");
|
|
$env[$key] = $val;
|
|
}
|
|
}
|
|
}
|
|
|
|
$hostname = $env['database.default.hostname'] ?? $env['database_default_hostname'] ?? 'localhost';
|
|
$database = $env['database.default.database'] ?? $env['database_default_database'] ?? '';
|
|
$username = $env['database.default.username'] ?? $env['database_default_username'] ?? 'root';
|
|
$password = $env['database.default.password'] ?? $env['database_default_password'] ?? '';
|
|
$port = (int) ($env['database.default.port'] ?? $env['database_default_port'] ?? 3306);
|
|
|
|
if ($database === '') {
|
|
echo "Database name tidak ditemukan di .env\n";
|
|
exit(1);
|
|
}
|
|
|
|
try {
|
|
$dsn = "mysql:host={$hostname};port={$port};dbname={$database};charset=utf8mb4";
|
|
$pdo = new PDO($dsn, $username, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
|
|
} catch (Throwable $e) {
|
|
echo "Koneksi DB gagal: " . $e->getMessage() . "\n";
|
|
exit(1);
|
|
}
|
|
|
|
$facesDir = $backendRoot . DIRECTORY_SEPARATOR . 'writable' . DIRECTORY_SEPARATOR . 'faces';
|
|
if (! is_dir($facesDir)) {
|
|
mkdir($facesDir, 0755, true);
|
|
echo "Folder dibuat: {$facesDir}\n";
|
|
}
|
|
|
|
$extensions = ['jpg', 'jpeg', 'png'];
|
|
$countOk = 0;
|
|
$countSkip = 0;
|
|
$countFail = 0;
|
|
|
|
$it = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
|
|
RecursiveIteratorIterator::SELF_FIRST
|
|
);
|
|
|
|
foreach ($it as $file) {
|
|
if (! $file->isFile()) {
|
|
continue;
|
|
}
|
|
$path = $file->getPathname();
|
|
$ext = strtolower($file->getExtension());
|
|
if (! in_array($ext, $extensions, true)) {
|
|
continue;
|
|
}
|
|
|
|
// Nama file bisa berupa "1234567890.jpg" atau "01. 1234567890.jpg"
|
|
$baseName = preg_replace('/\.(jpg|jpeg|png)$/i', '', $file->getFilename());
|
|
$baseName = trim($baseName);
|
|
if ($baseName === '') {
|
|
continue;
|
|
}
|
|
|
|
// Ambil deretan digit minimal 8 angka pertama sebagai NISN
|
|
$nisn = null;
|
|
if (preg_match('/(\d{8,})/', $baseName, $m) === 1) {
|
|
$nisn = $m[1];
|
|
}
|
|
if ($nisn === null || $nisn === '') {
|
|
echo " [skip] Tidak ditemukan NISN di nama file: {$baseName} ({$path})\n";
|
|
$countSkip++;
|
|
continue;
|
|
}
|
|
|
|
$stmt = $pdo->prepare('SELECT id FROM students WHERE nisn = ? LIMIT 1');
|
|
$stmt->execute([$nisn]);
|
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if (! $row) {
|
|
echo " [skip] NISN tidak ada di DB: {$nisn} ({$path})\n";
|
|
$countSkip++;
|
|
continue;
|
|
}
|
|
$studentId = (int) $row['id'];
|
|
$dest = $facesDir . DIRECTORY_SEPARATOR . $studentId . '.jpg';
|
|
|
|
// Resize + kompres dengan GD: lebar max 600px, quality 75
|
|
$srcImage = null;
|
|
if ($ext === 'jpg' || $ext === 'jpeg') {
|
|
$srcImage = @imagecreatefromjpeg($path);
|
|
} elseif ($ext === 'png') {
|
|
$srcImage = @imagecreatefrompng($path);
|
|
}
|
|
|
|
if ($srcImage === false || $srcImage === null) {
|
|
// Fallback: langsung copy jika GD gagal
|
|
if (copy($path, $dest)) {
|
|
$hash = md5_file($dest);
|
|
$update = $pdo->prepare('UPDATE students SET face_hash = ? WHERE id = ?');
|
|
$update->execute([$hash, $studentId]);
|
|
echo " [ok-copy] {$nisn} -> student_id {$studentId}\n";
|
|
$countOk++;
|
|
} else {
|
|
echo " [fail] Gagal copy (GD error): {$path} -> {$dest}\n";
|
|
$countFail++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
$width = imagesx($srcImage);
|
|
$height = imagesy($srcImage);
|
|
$maxWidth = 600;
|
|
if ($width > $maxWidth) {
|
|
$ratio = $maxWidth / $width;
|
|
$newWidth = $maxWidth;
|
|
$newHeight = (int) round($height * $ratio);
|
|
} else {
|
|
$newWidth = $width;
|
|
$newHeight = $height;
|
|
}
|
|
|
|
$dstImage = imagecreatetruecolor($newWidth, $newHeight);
|
|
imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
|
|
|
|
if (imagejpeg($dstImage, $dest, 75)) {
|
|
imagedestroy($srcImage);
|
|
imagedestroy($dstImage);
|
|
$hash = md5_file($dest);
|
|
$update = $pdo->prepare('UPDATE students SET face_hash = ? WHERE id = ?');
|
|
$update->execute([$hash, $studentId]);
|
|
echo " [ok] {$nisn} -> student_id {$studentId}\n";
|
|
$countOk++;
|
|
} else {
|
|
imagedestroy($srcImage);
|
|
imagedestroy($dstImage);
|
|
echo " [fail] Gagal save JPG: {$path} -> {$dest}\n";
|
|
$countFail++;
|
|
}
|
|
}
|
|
|
|
echo "\nSelesai. OK: {$countOk}, Skip (NISN tidak ada): {$countSkip}, Gagal: {$countFail}\n";
|