(BETA)
DEVICE-CAPTURE APP (BETA)
modif database-module by self-modification ya guys!
nb: database pada blogger/blogspot hanya TEMP Memory
DEVICE CAPTURE BY GEMINI OF GOOGLE
⚠️ Important Note for
Direct Camera Capture & Auto-Posting Queue
SOURCE CODE
To manage camera permissions directly in Chrome Settings for a specific website (such as
localhost or your hosted domain):Method 1: Change Permissions via Chrome Settings Menu
- Click the three dots (⋮) in the top right corner of Chrome and select Settings.
- Click on Privacy and security in the left sidebar.
- Click on Site settings.
- Under Permissions, click on Camera.
- Make sure "Sites can ask to use your camera" is toggled ON.
- Scroll down to Not allowed to use your camera:
- If your site URL (e.g.,
http://localhost:8000) is listed under Blocked, click on it and change the Camera option from Block to Allow.
Method 2: Reset All Site Permissions via Address Bar
If the site is running on
http://localhost or https://:- Click the Site Information / Tune icon (slider/lock icon) located at the far left of the address bar.
- Click Site settings.
- In the list that opens, find Camera and change the dropdown from Block / Ask to Allow.
- Return to your web tab and click Reset / Reload page.
⚠️ Important Note for file:/// URLs
Chrome's security architecture automatically hides
file:/// entries from the Chrome Camera permission settings page. If you are opening an .html file directly from your local folders without running a local web server (such as python -m http.server 8000), Chrome will not save camera exception rules for local files.Direct Capture & Auto-Posting Queue
Upload Queue (0)
Published Post Feed
SOURCE CODE
<html lang="en">
<head>
<meta charset="UTF-8"></meta>
<meta content="width=device-width, initial-scale=1.0" name="viewport"></meta>
<title>Direct Camera Capture & Auto-Posting Queue</title>
<style>
:root {
--bg: #0f172a;
--card-bg: #1e293b;
--accent: #2563eb;
--danger: #ef4444;
--text: #f8fafc;
--text-muted: #94a3b8;
--border: #334155;
--warning-bg: #451a03;
--warning-text: #fde047;
}
* { box-sizing: border-box; margin: 0; padding: 0; font-family: system-ui, -apple-system, sans-serif; }
body { background: var(--bg); color: var(--text); padding: 20px; display: flex; justify-content: center; }
.app-container { max-width: 900px; width: 100%; display: flex; flex-direction: column; gap: 20px; }
h1 { text-align: center; font-size: 1.5rem; color: var(--text); }
/* Camera Viewport */
.camera-card { background: var(--card-bg); border: 1px solid var(--border); border-radius: 12px; padding: 16px; text-align: center; }
.video-frame { width: 100%; max-height: 380px; background: #000; border-radius: 8px; overflow: hidden; position: relative; display: flex; align-items: center; justify-content: center; }
video#preview { width: 100%; height: 100%; max-height: 380px; object-fit: cover; }
.camera-error {
padding: 16px;
background: var(--warning-bg);
color: var(--warning-text);
border-radius: 8px;
font-size: 0.9rem;
margin-bottom: 12px;
display: none;
line-height: 1.5;
text-align: left;
}
.controls { display: flex; gap: 10px; justify-content: center; margin-top: 12px; flex-wrap: wrap; }
button, label.btn-upload { padding: 10px 18px; font-weight: 600; border: none; border-radius: 6px; cursor: pointer; transition: 0.2s; color: white; display: inline-flex; align-items: center; gap: 6px; font-size: 0.9rem; }
.btn-blue { background: var(--accent); }
.btn-red { background: var(--danger); }
.btn-gray { background: #475569; }
.btn-green { background: #166534; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
/* Two-column Layout for Queue & Post Page Feed */
.feed-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 640px) { .feed-layout { grid-template-columns: 1fr; } }
.column { background: var(--card-bg); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
.column h2 { font-size: 1.1rem; border-bottom: 1px solid var(--border); padding-bottom: 8px; margin-bottom: 12px; display: flex; justify-content: space-between; }
.media-list { display: flex; flex-direction: column; gap: 12px; }
.media-card { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 8px; }
.media-card img, .media-card video { width: 100%; max-height: 180px; border-radius: 6px; object-fit: cover; display: block; }
.badge { display: inline-block; font-size: 0.75rem; font-weight: bold; padding: 4px 8px; border-radius: 4px; margin-top: 6px; }
.status-queued { background: #854d0e; color: #fef08a; }
.status-uploading { background: #1e40af; color: #bfdbfe; }
.status-posted { background: #166534; color: #bbf7d0; }
</style>
</head>
<body>
<div class="app-container">
<h1>Direct Capture & Auto-Posting Queue</h1>
<!-- Live Camera Section -->
<div class="camera-card">
<div class="camera-error" id="cameraErrorBox"></div>
<div class="video-frame" id="videoFrameContainer">
<video autoplay="" id="preview" muted="" playsinline=""></video>
</div>
<div class="controls">
<button class="btn-gray" id="btnSwitchCam">Flip Camera 🔄</button>
<button class="btn-blue" id="btnCapturePhoto">Take Photo 📸</button>
<button class="btn-red" id="btnRecordVideo">Record Video 🎥</button>
<!-- Native Capture & File Selection -->
<label class="btn-green" for="deviceFileInput">
Upload File / Native Cam 📁
</label>
<input accept="image/*,video/*" capture="environment" id="deviceFileInput" multiple="" style="display: none;" type="file" />
</div>
</div>
<!-- Queue & Feed Section -->
<div class="feed-layout">
<!-- Media Queue Column -->
<div class="column">
<h2>Upload Queue <span id="queueCount" style="color: var(--text-muted); font-size: 0.9rem;">(0)</span></h2>
<div class="media-list" id="queueList"></div>
</div>
<!-- Live Post Page Feed Column -->
<div class="column">
<h2>Published Post Feed</h2>
<div class="media-list" id="postFeed"></div>
</div>
</div>
</div>
<script>
let currentStream = null;
let mediaRecorder = null;
let recordedChunks = [];
let facingMode = 'user';
let uploadQueue = [];
let isProcessingQueue = false;
const videoElement = document.getElementById('preview');
const cameraErrorBox = document.getElementById('cameraErrorBox');
const btnCapturePhoto = document.getElementById('btnCapturePhoto');
const btnRecordVideo = document.getElementById('btnRecordVideo');
const btnSwitchCam = document.getElementById('btnSwitchCam');
const deviceFileInput = document.getElementById('deviceFileInput');
const queueList = document.getElementById('queueList');
const postFeed = document.getElementById('postFeed');
const queueCountEl = document.getElementById('queueCount');
// Multi-tier Camera Initialization
async function initCamera() {
if (currentStream) {
currentStream.getTracks().forEach(track => track.stop());
}
// Secure Context Check
if (!window.isSecureContext && location.protocol !== 'file:') {
showError("<b>Security Notice:</b> Camera API requires HTTPS or Localhost. Running on an insecure HTTP IP address blocks webcam access.");
disableCamButtons();
return;
}
try {
// Step 1: Try requested video + audio stream
currentStream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: facingMode, width: { ideal: 1280 }, height: { ideal: 720 } },
audio: true
});
enableCamSuccess();
} catch (err1) {
try {
// Step 2: Fallback to video-only stream (if audio/mic was blocked)
currentStream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: facingMode }
});
enableCamSuccess();
} catch (err2) {
// Step 3: Handle specific camera errors
handleCamError(err2);
}
}
}
function enableCamSuccess() {
videoElement.srcObject = currentStream;
cameraErrorBox.style.display = 'none';
videoElement.style.display = 'block';
btnCapturePhoto.disabled = false;
btnRecordVideo.disabled = false;
btnSwitchCam.disabled = false;
}
function handleCamError(err) {
console.error("Camera error:", err);
let msg = "⚠️ <b>Camera Access Error:</b> ";
if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {
msg += "Browser or System permission was denied. Please allow camera permissions in browser site settings or use the <b>Upload File / Native Cam</b> button below.";
} else if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') {
msg += "No camera device found on this computer or phone.";
} else if (err.name === 'NotReadableError' || err.name === 'TrackStartError') {
msg += "Camera is currently in use by another application (e.g., Zoom, Teams, Skype).";
} else {
msg += err.message || "Unable to access live webcam.";
}
showError(msg);
disableCamButtons();
}
function showError(msg) {
cameraErrorBox.innerHTML = msg;
cameraErrorBox.style.display = 'block';
videoElement.style.display = 'none';
}
function disableCamButtons() {
btnCapturePhoto.disabled = true;
btnRecordVideo.disabled = true;
btnSwitchCam.disabled = true;
}
// Flip Camera
btnSwitchCam.addEventListener('click', () => {
facingMode = (facingMode === 'user') ? 'environment' : 'user';
initCamera();
});
// Capture Photo
btnCapturePhoto.addEventListener('click', () => {
if (!currentStream) return;
const canvas = document.createElement('canvas');
canvas.width = videoElement.videoWidth || 640;
canvas.height = videoElement.videoHeight || 480;
canvas.getContext('2d').drawImage(videoElement, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => {
const file = new File([blob], `photo_${Date.now()}.jpg`, { type: 'image/jpeg' });
addToQueue(file, 'image');
}, 'image/jpeg', 0.85);
});
// Toggle Video Recording
btnRecordVideo.addEventListener('click', () => {
if (!currentStream) return;
if (mediaRecorder && mediaRecorder.state === 'recording') {
mediaRecorder.stop();
btnRecordVideo.textContent = 'Record Video 🎥';
btnRecordVideo.className = 'btn-red';
} else {
recordedChunks = [];
try {
mediaRecorder = new MediaRecorder(currentStream, { mimeType: 'video/webm;codecs=vp9,opus' });
} catch (e) {
mediaRecorder = new MediaRecorder(currentStream);
}
mediaRecorder.ondataavailable = (e) => { if (e.data.size > 0) recordedChunks.push(e.data); };
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: mediaRecorder.mimeType || 'video/webm' });
const file = new File([blob], `video_${Date.now()}.webm`, { type: blob.type });
addToQueue(file, 'video');
};
mediaRecorder.start();
btnRecordVideo.textContent = 'Stop Recording 🛑';
btnRecordVideo.className = 'btn-gray';
}
});
// File Picker / Native Camera Input
deviceFileInput.addEventListener('change', (event) => {
const files = event.target.files;
for (let i = 0; i < files.length; i++) {
const file = files[i];
const type = file.type.startsWith('video') ? 'video' : 'image';
addToQueue(file, type);
}
deviceFileInput.value = '';
});
// Queue Operations
function addToQueue(file, type) {
const item = {
id: 'id_' + Date.now() + Math.random(),
file: file,
type: type,
url: URL.createObjectURL(file),
status: 'queued'
};
uploadQueue.push(item);
renderQueue();
processQueue();
}
function renderQueue() {
const activeItems = uploadQueue.filter(i => i.status !== 'posted');
queueCountEl.textContent = `(${activeItems.length})`;
queueList.innerHTML = '';
activeItems.forEach(item => {
const card = document.createElement('div');
card.className = 'media-card';
const media = item.type === 'image'
? `<img src="${item.url}">`
: `<video src="${item.url}" controls></video>`;
card.innerHTML = `${media}<span class="badge status-${item.status}">${item.status === 'queued' ? 'Queued' : 'Posting...'}</span>`;
queueList.appendChild(card);
});
}
// Automatic Queue Upload Process
async function processQueue() {
if (isProcessingQueue) return;
const item = uploadQueue.find(i => i.status === 'queued');
if (!item) { isProcessingQueue = false; return; }
isProcessingQueue = true;
item.status = 'uploading';
renderQueue();
try {
await new Promise(res => setTimeout(res, 2000));
item.status = 'posted';
renderQueue();
addToPostFeed(item);
} catch (err) {
item.status = 'queued';
} finally {
isProcessingQueue = false;
processQueue();
}
}
function addToPostFeed(item) {
const post = document.createElement('div');
post.className = 'media-card';
const media = item.type === 'image'
? `<img src="${item.url}">`
: `<video src="${item.url}" controls></video>`;
post.innerHTML = `${media}<span class="badge status-posted">Posted Automatically</span>`;
postFeed.insertBefore(post, postFeed.firstChild);
}
window.addEventListener('DOMContentLoaded', initCamera);
</script>
</body>
</html>
Comments
Post a Comment