Integrated barcode scanner via Webcam.

This commit is contained in:
Jan Böhmer 2020-04-28 18:36:30 +02:00
parent 1812e6f5bc
commit 48f4a360f4
6 changed files with 123 additions and 2 deletions

View file

@ -832,4 +832,9 @@ div.dataTables_wrapper div.dataTables_info {
.darkmode--activated img {
mix-blend-mode: difference;
}
.scanner-video {
max-width: 500px;
max-height: 250px;
}

View file

@ -23,6 +23,7 @@ import "marked";
import * as marked from "marked";
import "qrcode";
import {parse} from "marked";
import * as ZXing from "@zxing/library";
/************************************
*
@ -568,6 +569,83 @@ $(document).on("ajaxUI:reload", function() {
})
});
//Init barcode scanner
$(document).on("ajaxUI:start ajaxUI:reload", function() {
//Skip if we are not on scanner page...
if (!document.getElementById('scan_dialog_form')) {
return;
}
let selectedDeviceId;
const codeReader = new ZXing.BrowserMultiFormatReader();
console.log('ZXing code reader initialized');
codeReader.listVideoInputDevices()
.then((videoInputDevices) => {
if (videoInputDevices.length >= 1) {
const sourceSelect = document.getElementById('sourceSelect');
videoInputDevices.forEach((element) => {
const sourceOption = document.createElement('option');
sourceOption.text = element.label;
sourceOption.value = element.deviceId;
sourceSelect.appendChild(sourceOption);
});
//Try to retrieve last selected webcam...
let last_cam_id = localStorage.getItem('scanner_last_cam_id');
if (!!last_cam_id) {
//selectedDeviceId = localStorage.getItem('scanner_last_cam_id');
$(sourceSelect).val(last_cam_id);
} else {
selectedDeviceId = videoInputDevices[0].deviceId;
}
sourceSelect.onchange = () => {
//@ts-ignore
selectedDeviceId = sourceSelect.value;
localStorage.setItem('scanner_last_cam_id', selectedDeviceId);
changeHandler();
};
document.getElementById('sourceSelectPanel').classList.remove('d-none');
document.getElementById('video').classList.remove('d-none');
document.getElementById('scanner-warning').classList.add('d-none');
}
let changeHandler = () => {
codeReader.reset();
codeReader.decodeFromVideoDevice(selectedDeviceId, 'video', (result, err) => {
if (result) {
//@ts-ignore
document.getElementById('scan_dialog_input').value = result.text;
//Submit form
//@ts-ignore
document.getElementById('scan_dialog_form').submit();
}
if (err && !(err instanceof ZXing.NotFoundException)) {
console.error(err);
//document.getElementById('result').textContent = err
}
});
console.log(`Started continous decode from camera with id ${selectedDeviceId}`)
};
//Register Change Src Button
//document.getElementById('changeSrcBtn').addEventListener('click', changeHandler);
//Try to start logging automatically.
changeHandler();
})
.catch((err) => {
console.error(err)
})
});
//Need for proper body padding, with every navbar height
$(window).resize(function () {

View file

@ -25,6 +25,7 @@
"@types/jquery.form": "^3.26.30",
"@types/marked": "^0.7.2",
"@types/typeahead": "^0.11.32",
"@zxing/library": "^0.16.3",
"bootbox": "^5.4.0",
"bootstrap-fileinput": "^5.0.1",
"bootstrap-select": "^1.13.8",

View file

@ -34,7 +34,8 @@ class ScanDialogType extends AbstractType
$builder->add('input', TextType::class, [
'attr' => [
'autofocus' => true,
]
'id' => 'scan_dialog_input',
],
]);
$builder->add('submit', SubmitType::class, [

View file

@ -3,7 +3,31 @@
{% block card_title %}<i class="fas fa-atom fa-fw"></i> {% trans %}label_scanner.title{% endtrans %}{% endblock %}
{% block card_content %}
{{ form_start(form) }}
<div class="alert alert-warning" id="scanner-warning">
<strong>{% trans %}label_scanner.no_cam_found.title{% endtrans %}</strong>: {% trans %}label_scanner.no_cam_found.text{% endtrans %}
</div>
{{ form_start(form, {'attr': {'id': 'scan_dialog_form'}}) }}
{{ form_end(form) }}
<div class="">
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<video id="video" width="100%" height="auto" class="scanner-video img-thumbnail d-none">
</video>
</div>
</div>
{# <div>
<button type="button" class="btn btn-secondary" id="changeSrcBtn">Change Source</button>
</div>#}
<div id="sourceSelectPanel" class="form-group row d-none">
<label for="sourceSelect" class="col-sm-3 col-form-label">{% trans %}label_scanner.source_select{% endtrans %}</label>
<div class="col-sm-9">
<select id="sourceSelect" style="" class="form-control"></select>
</div>
</div>
</div>
{% endblock %}

View file

@ -1018,6 +1018,13 @@
resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
"@zxing/library@^0.16.3":
version "0.16.3"
resolved "https://registry.yarnpkg.com/@zxing/library/-/library-0.16.3.tgz#3d9d2a031f03b01b67ee6defc232aae106329f84"
integrity sha512-GjhP1eWfj76fn8JnipDXKEErgfNJO8CJb0bEgkZ14RLSo1z2U1wD7RUt609oRWYYq2gPQfK3X+Z159K4ecNzHQ==
dependencies:
ts-custom-error "^3.0.0"
accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
@ -6686,6 +6693,11 @@ toidentifier@1.0.0:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
ts-custom-error@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/ts-custom-error/-/ts-custom-error-3.1.1.tgz#d30c7415461dac93dc2cc9e9eb2dae92e6423901"
integrity sha512-f/syoy+pTE4z82qaiRuthEeZtCGNKzlfs0Zc8jpQFcz/CYMaFSwFSdfFt1sSFnPlDLOEm7RCROdIxZ44N8UlwA==
ts-loader@^5.3.0:
version "5.4.5"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-5.4.5.tgz#a0c1f034b017a9344cef0961bfd97cc192492b8b"