(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

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

  1. Click the three dots (⋮) in the top right corner of Chrome and select Settings.

  2. Click on Privacy and security in the left sidebar.

  3. Click on Site settings.

  4. Under Permissions, click on Camera.

  5. Make sure "Sites can ask to use your camera" is toggled ON.

  6. 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://:

  1. Click the Site Information / Tune icon (slider/lock icon) located at the far left of the address bar.

  2. Click Site settings.

  3. In the list that opens, find Camera and change the dropdown from Block / Ask to Allow.

  4. 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 Camera Capture & Auto-Posting Queue

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 &amp; 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

Popular posts from this blog

[MAIN PROGRAM] UNDONE HOUSEHOLD WORKs WEB-PROGRAM v2/MAIN

PENDING ITEMs [REVISION] v0