Scan barcodes and display releases
This commit is contained in:
@@ -1,28 +1,26 @@
|
|||||||
import { BarcodeDetector } from "barcode-detector/ponyfill";
|
import { BarcodeDetector } from "barcode-detector/ponyfill";
|
||||||
|
|
||||||
const barcodeTest = async function () {
|
const barcodeReaderSetup = async function () {
|
||||||
// check supported formats
|
|
||||||
const supportedFormats = await BarcodeDetector.getSupportedFormats();
|
const supportedFormats = await BarcodeDetector.getSupportedFormats();
|
||||||
console.log(supportedFormats);
|
|
||||||
|
|
||||||
const barcodeDetector = new BarcodeDetector({
|
return new BarcodeDetector({
|
||||||
// make sure the formats are supported
|
// make sure the formats are supported
|
||||||
formats: ["qr_code"],
|
formats: supportedFormats,
|
||||||
});
|
});
|
||||||
|
|
||||||
const imageFile = await fetch(
|
|
||||||
"https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=Hello%20world!",
|
|
||||||
).then((resp) => resp.blob());
|
|
||||||
|
|
||||||
barcodeDetector.detect(imageFile).then(console.log);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mounted() {
|
async mounted() {
|
||||||
|
const detectedBarcodes = new Set([]);
|
||||||
|
const barcodeDetector = await barcodeReaderSetup();
|
||||||
const video = this.el;
|
const video = this.el;
|
||||||
const constraints = {
|
const constraints = {
|
||||||
audio: false,
|
audio: false,
|
||||||
video: { width: 800, height: 600 },
|
video: {
|
||||||
|
width: 800, height: 600, facingMode: {
|
||||||
|
ideal: "environment"
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
navigator.mediaDevices
|
navigator.mediaDevices
|
||||||
@@ -30,10 +28,20 @@ export default {
|
|||||||
.then((mediaStream) => {
|
.then((mediaStream) => {
|
||||||
console.debug("Camera access allowed")
|
console.debug("Camera access allowed")
|
||||||
this.pushEventTo(video, "camera:allowed", {});
|
this.pushEventTo(video, "camera:allowed", {});
|
||||||
console.log(mediaStream);
|
|
||||||
video.srcObject = mediaStream;
|
video.srcObject = mediaStream;
|
||||||
video.onloadedmetadata = () => {
|
video.onloadedmetadata = () => {
|
||||||
video.play();
|
video.play();
|
||||||
|
this.scanInterval = window.setInterval(async () => {
|
||||||
|
const barcodes = await barcodeDetector.detect(video);
|
||||||
|
if (barcodes.length <= 0) return;
|
||||||
|
|
||||||
|
barcodes.forEach(barcode => {
|
||||||
|
if (!detectedBarcodes.has(barcode.rawValue)) {
|
||||||
|
this.pushEventTo(video, "barcode:scanned", { number: barcode.rawValue });
|
||||||
|
detectedBarcodes.add(barcode.rawValue);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, 500)
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@@ -43,5 +51,6 @@ export default {
|
|||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.el.srcObject.getTracks().forEach(track => track.stop());
|
this.el.srcObject.getTracks().forEach(track => track.stop());
|
||||||
|
window.clearInterval(this.scanInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,6 +305,15 @@ defmodule MusicBrainz.APIImpl do
|
|||||||
json_get(url, config)
|
json_get(url, config)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def search_release_by_barcode(barcode, config) do
|
||||||
|
url =
|
||||||
|
@base_url <> "/release?query=barcode:#{barcode}&fmt=json"
|
||||||
|
|
||||||
|
with {:ok, result} <- json_get(url, config) do
|
||||||
|
{:ok, result["releases"]}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Uses the [search](https://musicbrainz.org/doc/MusicBrainz_API/Search#Release_Group) endpoint with a search query string.
|
Uses the [search](https://musicbrainz.org/doc/MusicBrainz_API/Search#Release_Group) endpoint with a search query string.
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,16 @@ defmodule MusicLibrary.Records do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def search_release_by_barcode(barcode) do
|
||||||
|
case music_brainz_config().api.search_release_by_barcode(barcode, music_brainz_config()) do
|
||||||
|
{:ok, releases} ->
|
||||||
|
{:ok, releases}
|
||||||
|
|
||||||
|
error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def import_from_musicbrainz_release_group(musicbrainz_id, opts \\ []) do
|
def import_from_musicbrainz_release_group(musicbrainz_id, opts \\ []) do
|
||||||
with format = Keyword.get(opts, :format, "cd"),
|
with format = Keyword.get(opts, :format, "cd"),
|
||||||
purchased_at = Keyword.get(opts, :purchased_at),
|
purchased_at = Keyword.get(opts, :purchased_at),
|
||||||
|
|||||||
@@ -1,14 +1,32 @@
|
|||||||
defmodule MusicLibraryWeb.RecordLive.BarcodeScannerComponent do
|
defmodule MusicLibraryWeb.RecordLive.BarcodeScannerComponent do
|
||||||
use MusicLibraryWeb, :live_component
|
use MusicLibraryWeb, :live_component
|
||||||
|
|
||||||
|
alias MusicLibrary.Records
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def mount(socket) do
|
||||||
|
{:ok,
|
||||||
|
socket
|
||||||
|
|> assign(:camera, :pending)
|
||||||
|
|> assign(:barcodes, MapSet.new())
|
||||||
|
|> assign(:releases, [])}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div>
|
<div>
|
||||||
<h1>Barcode Scan</h1>
|
<h1>Barcode Scan</h1>
|
||||||
<video id="barcode-scan" phx-hook="BarcodeScanner"></video>
|
<video id="camera-view" phx-hook="BarcodeScanner"></video>
|
||||||
|
<ul>
|
||||||
|
<li :for={release <- assigns.releases}>
|
||||||
|
<span>{release["id"]}</span>
|
||||||
|
<span>{release["barcode"]}</span>
|
||||||
|
<span>{release["title"]}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
@@ -23,4 +41,18 @@ defmodule MusicLibraryWeb.RecordLive.BarcodeScannerComponent do
|
|||||||
Logger.debug(fn -> "Camera access denied" end)
|
Logger.debug(fn -> "Camera access denied" end)
|
||||||
{:noreply, assign(socket, camera: :denied)}
|
{:noreply, assign(socket, camera: :denied)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("barcode:scanned", %{"number" => number}, socket) do
|
||||||
|
Logger.debug(fn -> "Scanned barcode #{number}" end)
|
||||||
|
|
||||||
|
{:ok, releases} =
|
||||||
|
Records.search_release_by_barcode(number)
|
||||||
|
|
||||||
|
socket =
|
||||||
|
socket
|
||||||
|
|> assign(barcodes: MapSet.put(socket.assigns.barcodes, number))
|
||||||
|
|> assign(:releases, releases ++ socket.assigns.releases)
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user