Zum Inhalt springen

LabelScan: Unterschied zwischen den Versionen

Aus ADOS Wiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 29: Zeile 29:
</div>
</div>
</html>
</html>
{{#tag:html|
<script>
(function(){
  document.addEventListener('DOMContentLoaded',function(){
    var run  = document.getElementById('ados-scan-run');
    var file = document.getElementById('ados-scan-file');
    var big  = document.getElementById('ados-scan-bigbtn');
    var prev = document.getElementById('ados-scan-preview');
    var stat = document.getElementById('ados-scan-status');
    var prog = document.getElementById('ados-scan-progress');
    if(!run || !file){ console.warn('[LabelScan] UI-Elemente fehlen'); return; }
    function setStatus(t){ stat.textContent = t || ''; }
    function setProgress(p){
      if(p==null){ prog.style.display='none'; prog.value=0; return; }
      prog.style.display=''; prog.value=Math.max(0,Math.min(1,p));
    }
    function showPreview(f){
      var url = URL.createObjectURL(f);
      prev.innerHTML = '<img alt="Vorschau" style="max-width:100%;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,.06)" src="'+url+'">';
      prev.setAttribute('aria-hidden','false');
    }
    // großer Button öffnet echte Dateiauswahl
    if (big) big.addEventListener('click', function(){ file.click(); });
    // Vorschau bei Auswahl
    file.addEventListener('change', function(){ if (this.files && this.files[0]) showPreview(this.files[0]); });
    // Drag&Drop (optional, falls du #ados-scan-drop nutzt)
    var drop = document.getElementById('ados-scan-drop');
    if (drop){
      drop.addEventListener('dragover', function(e){ e.preventDefault(); drop.classList.add('dragover'); });
      drop.addEventListener('dragleave', function(){ drop.classList.remove('dragover'); });
      drop.addEventListener('drop', function(e){
        e.preventDefault(); drop.classList.remove('dragover');
        if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length){
          file.files = e.dataTransfer.files;
          showPreview(file.files[0]);
        }
      });
    }
    // Tesseract lazy-load mit Fallback-CDN
    function loadTesseract(){
      return new Promise(function(resolve,reject){
        if (window.Tesseract) return resolve();
        var s=document.createElement('script');
        s.src='https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js';
        s.async=true;
        s.onload=resolve;
        s.onerror=function(){
          var s2=document.createElement('script');
          s2.src='https://unpkg.com/tesseract.js@5/dist/tesseract.min.js';
          s2.async=true;
          s2.onload=resolve;
          s2.onerror=function(){ reject(new Error('Tesseract konnte nicht geladen werden')); };
          document.head.appendChild(s2);
        };
        document.head.appendChild(s);
      });
    }
    // Heuristik -> Query
    function extractHints(text){
      var raw  = (text||'').replace(/\s+/g,' ').trim();
      var words = (raw.match(/\b[A-ZÄÖÜ][A-Za-zÄÖÜäöüß\-]{3,}\b/g) || []).slice(0,5);
      var ages  = (raw.match(/\b([1-9]\d?)\s?(?:years?|yo|jahr|jahre)\b/gi) || []).map(function(s){ var m=s.match(/[1-9]\d?/); return m?m[0]:null; }).filter(Boolean);
      var years = (raw.match(/\b(19|20)\d{2}\b/g) || []);
      return {words:words, ages:ages, years:years};
    }
    function buildSearchQuery(h){
      var parts=[];
      h.words.forEach(function(w){ parts.push('"' + w + '"'); });
      h.ages.forEach(function(a){ parts.push('"' + a + '"'); });
      h.years.forEach(function(y){ parts.push('"' + y + '"'); });
      if (!parts.length) parts.push('Whisky');
      return parts.join(' ');
    }
    function renderResults(items){
      var wrap = document.getElementById('ados-scan-results');
      wrap.innerHTML = '';
      if (!items.length){
        wrap.innerHTML = '<div class="ados-hit">Keine klaren Treffer. Bitte anderes Foto oder manuell suchen.</div>';
        return;
      }
      items.slice(0,8).forEach(function(h){
        var title = h.title || h.Seitentitel || '';
        var link  = mw.util.getUrl( title.replace(/ /g,'_') );
        var snip  = (h.snippet||'').replace(/<\/?span[^>]*>/g,'').replace(/&quot;/g,'"');
        var div  = document.createElement('div');
        div.className = 'ados-hit';
        div.style.border='1px solid #eee'; div.style.borderRadius='10px'; div.style.padding='10px'; div.style.background='#fafafa';
        div.innerHTML = '<b><a href="'+link+'">'+mw.html.escape(title)+'</a></b>' + (snip ? '<div class="meta" style="color:#666">'+snip+'</div>' : '');
        wrap.appendChild(div);
      });
    }
    // Klick-Handler: Erkennen & suchen
    run.addEventListener('click', function(ev){
      ev.preventDefault();
      if (!(file.files && file.files[0])){ alert('Bitte ein Foto auswählen oder aufnehmen.'); return; }
      var f = file.files[0];
      (async function(){
        try{
          run.disabled=true; run.textContent='Erkenne …';
          setStatus('Erkenne Label …');
          await loadTesseract();
          setProgress(0);
          var result = await Tesseract.recognize(f, 'deu+eng', {
            logger: function(m){
              if (m && m.status === 'recognizing text' && typeof m.progress === 'number') setProgress(m.progress);
            }
          });
          setProgress(null);
          setStatus('Suche im Wiki …');
          var text  = (result && result.data && result.data.text) || '';
          var hints = extractHints(text);
          var query = buildSearchQuery(hints);
          mw.loader.using('mediawiki.api').then(async function(){
            var api = new mw.Api();
            var r  = await api.get({ action:'query', list:'search', srsearch:query, srlimit:10, formatversion:2 });
            var hits = (r.query && r.query.search) || [];
            renderResults(hits);
            setStatus('Fertig.');
            run.disabled=false; run.textContent='Erkennen & suchen';
          });
        } catch(e){
          console.error('[LabelScan] Fehler:', e);
          setProgress(null);
          setStatus('Fehler bei Erkennung/Suche. Bitte erneut versuchen.');
          run.disabled=false; run.textContent='Erkennen & suchen';
        }
      })();
    });
    console.log('LabelScan inline bound');
  });
})();
</script>
}}

Version vom 5. November 2025, 19:06 Uhr

📸 Abfüllung scannen

Foto der Front-Labelseite aufnehmen oder wählen. Die Erkennung läuft vollständig lokal im Browser.

Bereit.

Vorschläge