Initial commit
This commit is contained in:
commit
263f498563
13 changed files with 478 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Caddyfile
|
18
Caddyfile_example
Normal file
18
Caddyfile_example
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
admin off
|
||||
}
|
||||
|
||||
http://0.0.0.0:8001 {
|
||||
root * ./static
|
||||
file_server
|
||||
header {
|
||||
X-Frame-Options DENY
|
||||
X-Content-Type-Options nosniff
|
||||
X-Permitted-Cross-Domain-Policies none
|
||||
Referrer-Policy no-referrer
|
||||
Cross-Origin-Embedder-Policy require-corp
|
||||
Cross-Origin-Opener-Policy same-origin
|
||||
Permissions-Policy accelerometer=(),ambient-light-sensor=(),autoplay=(),battery=(),camera=(),display-capture=(),document-domain=(),encrypted-media=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),layout-animations=(self),legacy-image-formats=(self),magnetometer=(),microphone=(self),midi=(),oversized-images=(self),payment=(),picture-in-picture=(),publickey-credentials-get=(),speaker-selection=(),sync-xhr=(self),unoptimized-images=(self),unsized-media=(self),usb=(),screen-wake-lock=(),web-share=(),xr-spatial-tracking=()
|
||||
Cache-Control max-age=86400
|
||||
}
|
||||
}
|
21
static/index.html
Normal file
21
static/index.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Copyright (c) 2024 Julian Müller (ChaoticByte) -->
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" href="favicon.png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="main.css">
|
||||
<title>Transcript Frontend</title>
|
||||
</head>
|
||||
<body>
|
||||
<button class="startBtn symbtn nodisplay" id="startBtn"></button>
|
||||
<button class="stopBtn symbtn nodisplay" id="stopBtn"></button>
|
||||
<div class="horizontal">
|
||||
<audio class="nodisplay" id="audioPrev" controls></audio>
|
||||
<button class="transcribeBtn symbtn nodisplay" id="transcribeBtn"></button>
|
||||
</div>
|
||||
<div class="transcript" id="transcript"></div>
|
||||
<script src="main.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
118
static/main.css
Normal file
118
static/main.css
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* Copyright (c) 2024 Julian Müller (ChaoticByte) */
|
||||
:root {
|
||||
--text-color: #fffffc;
|
||||
--box-bg: #ffffff25;
|
||||
--btn-bg: #ffffff3b;
|
||||
--border-color: #707070;
|
||||
--corner-radius: .4rem;
|
||||
}
|
||||
body {
|
||||
/* layout */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
/* coloring */
|
||||
color: var(--text-color);
|
||||
background: linear-gradient(30deg, #0d141d, #1d192e);
|
||||
background-size: contain;
|
||||
/* font stuff */
|
||||
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: inherit;
|
||||
}
|
||||
.symbtn {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background-size: 2.5rem;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
border-radius: var(--corner-radius);
|
||||
background-color: transparent;
|
||||
transition: background-color 100ms;
|
||||
transition: transform 100ms;
|
||||
}
|
||||
.symbtn:hover {
|
||||
background-color: var(--btn-bg);
|
||||
}
|
||||
.symbtn:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
.symbtn:disabled {
|
||||
opacity: 70%;
|
||||
transform: none;
|
||||
background-color: transparent;
|
||||
cursor: initial;
|
||||
}
|
||||
.nodisplay {
|
||||
display: none;
|
||||
}
|
||||
.horizontal {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
gap: .5rem;
|
||||
}
|
||||
.startBtn, .stopBtn {
|
||||
margin-top: 5rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
.startBtn {
|
||||
background-image: url("material-icons/mic.svg");
|
||||
}
|
||||
.startBtn:hover {
|
||||
background-color: red;
|
||||
}
|
||||
.stopBtn {
|
||||
background-image: url("material-icons/stop.svg");
|
||||
}
|
||||
.transcribeBtn {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background-size: 2rem;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("material-icons/transcribe.svg");
|
||||
}
|
||||
.transcript {
|
||||
margin-top: 5rem;
|
||||
display: block;
|
||||
width: 60%;
|
||||
height: fit-content;
|
||||
text-align: center;
|
||||
}
|
||||
.transcript.loading {
|
||||
font-size: 0;
|
||||
background-image: url("material-icons/cycle.svg");
|
||||
background-size: contain;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
opacity: 70%;
|
||||
animation: spinning 3s infinite linear;
|
||||
}
|
||||
@keyframes spinning {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
.transcript {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
63
static/main.js
Normal file
63
static/main.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2024 Julian Müller (ChaoticByte)
|
||||
"use strict";
|
||||
|
||||
import { Recorder } from "./recorder.js";
|
||||
|
||||
async function api_is_online(settings) {
|
||||
const ping_response = await fetch(settings.api_url + "/ping").catch(_ => {
|
||||
return 0;
|
||||
});
|
||||
return ping_response.status == 200;
|
||||
}
|
||||
|
||||
( async () => {
|
||||
// Get Elements
|
||||
let startBtn = document.getElementById("startBtn");
|
||||
let stopBtn = document.getElementById("stopBtn");
|
||||
let audioPrev = document.getElementById("audioPrev");
|
||||
let transcribeBtn = document.getElementById("transcribeBtn");
|
||||
let transcriptText = document.getElementById("transcript");
|
||||
// Load Settings
|
||||
const settings_resp = await fetch("settings.json");
|
||||
const settings = await settings_resp.json();
|
||||
while (settings.api_url.endsWith('/')) {
|
||||
settings.api_url = settings.api_url.substring(0, settings.api_url.length - 1);
|
||||
}
|
||||
if (await api_is_online(settings)) {
|
||||
// Recorder
|
||||
let audioBlob;
|
||||
const recorder = new Recorder(startBtn, stopBtn, async (blob) => {
|
||||
audioBlob = blob;
|
||||
audioPrev.src = URL.createObjectURL(audioBlob);
|
||||
audioPrev.load();
|
||||
audioPrev.classList.remove("nodisplay");
|
||||
transcribeBtn.classList.remove("nodisplay");
|
||||
});
|
||||
recorder.init();
|
||||
// Additional handlers
|
||||
startBtn.addEventListener("click", () => {
|
||||
audioPrev.classList.add("nodisplay");
|
||||
transcribeBtn.classList.add("nodisplay");
|
||||
transcriptText.innerText = "";
|
||||
})
|
||||
// Transcribe
|
||||
transcribeBtn.addEventListener("click", async () => {
|
||||
transcribeBtn.disabled = true;
|
||||
transcriptText.classList.add("loading");
|
||||
const formData = new FormData();
|
||||
formData.append("audio", audioBlob);
|
||||
let response = await fetch(settings.api_url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
let t = await response.text();
|
||||
transcriptText.innerText = t;
|
||||
transcriptText.classList.remove("loading");
|
||||
transcribeBtn.disabled = false;
|
||||
});
|
||||
// init done.
|
||||
startBtn.classList.remove("nodisplay");
|
||||
} else {
|
||||
transcriptText.innerText = "Can't reach API :("
|
||||
}
|
||||
})();
|
206
static/material-icons/LICENSE
Normal file
206
static/material-icons/LICENSE
Normal file
|
@ -0,0 +1,206 @@
|
|||
|
||||
Material Icons by Google.
|
||||
Repo: https://github.com/google/material-design-icons
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
1
static/material-icons/add.svg
Normal file
1
static/material-icons/add.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-440H240q-17 0-28.5-11.5T200-480q0-17 11.5-28.5T240-520h200v-200q0-17 11.5-28.5T480-760q17 0 28.5 11.5T520-720v200h200q17 0 28.5 11.5T760-480q0 17-11.5 28.5T720-440H520v200q0 17-11.5 28.5T480-200q-17 0-28.5-11.5T440-240v-200Z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 347 B |
1
static/material-icons/cycle.svg
Normal file
1
static/material-icons/cycle.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M160-479q0 85 42.5 158T318-204q14 9 19.5 24.5T335-150q-8 15-24.5 19.5T279-134q-93-54-146-146T80-479q0-26 3.5-51t9.5-50l-13 8q-14 9-30 4.5T26-586q-8-14-3.5-30.5T41-641l121-70q14-8 30.5-3.5T217-696l70 120q8 14 3.5 30.5T272-521q-14 8-30.5 3.5T217-536l-34-59q-11 28-17 57t-6 59Zm320-321q-41 0-81 10.5T323-759q-15 8-31.5 5.5T267-770q-9-16-4-32.5t21-25.5q45-26 94.5-39T480-880q79 0 151.5 29.5T761-765v-15q0-17 11.5-28.5T801-820q17 0 28.5 11.5T841-780v140q0 17-11.5 28.5T801-600H661q-17 0-28.5-11.5T621-640q0-17 11.5-28.5T661-680h69q-46-57-111-88.5T480-800Zm242 531q38-44 58-97t20-111q0-17 11.5-30t28.5-13q17 0 28.5 13t11.5 30q0 65-20.5 125.5T800-239q-39 52-92.5 89T591-95l10 6q14 8 18 24.5T615-34q-8 14-24 18t-30-4L439-90q-14-8-18.5-24.5T424-145l70-121q8-14 24-18t30 4q14 8 18.5 24.5T563-225l-37 63q57-8 107.5-35.5T722-269Z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 935 B |
1
static/material-icons/mic.svg
Normal file
1
static/material-icons/mic.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-400q-50 0-85-35t-35-85v-240q0-50 35-85t85-35q50 0 85 35t35 85v240q0 50-35 85t-85 35Zm-40 240v-83q-92-13-157.5-78T203-479q-2-17 9-29t28-12q17 0 28.5 11.5T284-480q14 70 69.5 115T480-320q72 0 127-45.5T676-480q4-17 15.5-28.5T720-520q17 0 28 12t9 29q-14 91-79 157t-158 79v83q0 17-11.5 28.5T480-120q-17 0-28.5-11.5T440-160Z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 440 B |
1
static/material-icons/stop.svg
Normal file
1
static/material-icons/stop.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M360-320h240q17 0 28.5-11.5T640-360v-240q0-17-11.5-28.5T600-640H360q-17 0-28.5 11.5T320-600v240q0 17 11.5 28.5T360-320ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 426 B |
1
static/material-icons/transcribe.svg
Normal file
1
static/material-icons/transcribe.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M850-750q6-6 14.5-9.5T882-763q19 0 32 13t13 32q0 9-3.5 17.5T914-686q-18 18-29 38.5T874-600q0 25 10.5 46t27.5 38q8 7 11.5 15.5T927-482q0 19-13 32t-32 13q-9 0-17.5-3.5T850-450q-32-29-50-67.5T782-600q0-44 18-82.5t50-67.5ZM722-878q6-6 14-9.5t17-3.5q19 0 32 13t13 32q0 10-4 18.5T783-813q-43 41-68 95.5T690-600q0 63 24.5 118t67.5 96q7 6 11 14.5t4 18.5q0 18-13 31t-31 13q-9 0-17-3.5t-14-9.5q-56-53-89-125t-33-153q0-81 33-153t89-125ZM360-440q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM40-200v-32q0-33 17-62t47-44q51-26 115-44t141-18q77 0 141 18t115 44q30 15 47 44t17 62v32q0 33-23.5 56.5T600-120H120q-33 0-56.5-23.5T40-200Z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 770 B |
43
static/recorder.js
Normal file
43
static/recorder.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2024 Julian Müller (ChaoticByte)
|
||||
"use strict";
|
||||
|
||||
export class Recorder {
|
||||
constructor(startBtn, stopBtn, callback) {
|
||||
this.supported = navigator.mediaDevices && navigator.mediaDevices.getUserMedia;
|
||||
console.assert(this.supported, "Recording Audio is not supported on this device.");
|
||||
this.startBtn = startBtn;
|
||||
this.stopBtn = stopBtn;
|
||||
this.callback = callback;
|
||||
this.initialized = false;
|
||||
}
|
||||
async init() {
|
||||
console.assert(!this.initialized, "Tried to initialize Recorder that was already initialized.");
|
||||
if (this.initialized) { return null }
|
||||
await navigator.mediaDevices.getUserMedia({"audio": true}).then(stream => {
|
||||
let chunks = [];
|
||||
this.startBtn.classList.remove("nodisplay");
|
||||
this.stopBtn.classList.add("nodisplay");
|
||||
const recorder = new MediaRecorder(stream);
|
||||
this.startBtn.onclick = () => {
|
||||
this.startBtn.classList.add("nodisplay");
|
||||
this.stopBtn.classList.remove("nodisplay");
|
||||
recorder.start();
|
||||
}
|
||||
this.stopBtn.onclick = () => {
|
||||
recorder.stop();
|
||||
}
|
||||
recorder.onstart = () => {
|
||||
chunks = []; // reset chunks on new recording
|
||||
}
|
||||
recorder.onstop = e => {
|
||||
const blob = new Blob(chunks, {type: "audio/ogg; codecs=opus"})
|
||||
this.startBtn.classList.remove("nodisplay");
|
||||
this.stopBtn.classList.add("nodisplay");
|
||||
this.callback(blob);
|
||||
}
|
||||
recorder.ondataavailable = e => {
|
||||
chunks.push(e.data);
|
||||
}
|
||||
}, () => { console.error("Could not initialize Audio Recorder.") })
|
||||
}
|
||||
}
|
3
static/settings.json
Normal file
3
static/settings.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"api_url": "http://127.0.0.1:8000"
|
||||
}
|
Reference in a new issue