Upload files to "/"
This commit is contained in:
commit
839524bf38
|
@ -0,0 +1,11 @@
|
|||
module tts-stuff
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/bwmarrin/discordgo v0.27.1
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
|
||||
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
@ -0,0 +1,261 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<style>
|
||||
:root {
|
||||
--acolor: black;
|
||||
}
|
||||
|
||||
@supports (color-scheme: dark only) {
|
||||
:root[data-darkmode='f'] {
|
||||
color-scheme: dark only;
|
||||
--acolor: white;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
a,
|
||||
a:link,
|
||||
a:visited,
|
||||
a:hover,
|
||||
a:focus,
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
color: var(--acolor);
|
||||
}
|
||||
|
||||
hr {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#div1 {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
@supports (max-width: 100dvw) {
|
||||
#div1 {
|
||||
height: 100dvh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
if (
|
||||
CSS.supports('color-scheme', 'dark only') &&
|
||||
location.hash !== '#forceDarkMode=0'
|
||||
) {
|
||||
document.documentElement.dataset.darkmode = 'f'
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="padding: 0; margin: 0">
|
||||
<div
|
||||
id="div1"
|
||||
style="
|
||||
display: grid;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
grid-template-rows: auto auto auto 1fr;
|
||||
"
|
||||
>
|
||||
<hr />
|
||||
<div
|
||||
style="
|
||||
display: grid;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
"
|
||||
>
|
||||
<div style="overflow-x: hidden; overflow-y: auto">
|
||||
Load file
|
||||
<br />
|
||||
<input type="file" id="file" autocomplete="off" />
|
||||
</div>
|
||||
<div style="display: grid; place-items: center end">
|
||||
<span>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="forceDarkMode"
|
||||
autocomplete="off"
|
||||
disabled
|
||||
/>
|
||||
<label for="forceDarkMode">Force dark mode</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<textarea style="resize: none" autocomplete="off"></textarea>
|
||||
<hr />
|
||||
<div style="display: grid; place-items: center">
|
||||
<span>
|
||||
<button id="awb">awb</button>
|
||||
<button id="kal">kal</button>
|
||||
<button id="kal16">kal16</button>
|
||||
<button id="rms">rms</button>
|
||||
<button id="slt">slt</button>
|
||||
</span>
|
||||
</div>
|
||||
<hr />
|
||||
<div style="display: grid; place-items: center">
|
||||
<a href="https://git.projectsegfau.lt/a/tts-stuff" target="_blank"
|
||||
>Source code</a
|
||||
>
|
||||
<a href="license.txt" target="_blank">License (MIT)</a>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
<div
|
||||
id="div2"
|
||||
style="
|
||||
display: none;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
place-items: start center;
|
||||
"
|
||||
>
|
||||
<hr />
|
||||
<span id="audioSpan"></span>
|
||||
<hr />
|
||||
<span id="durationSpan"></span>
|
||||
<hr />
|
||||
<span id="saveSpan"></span>
|
||||
<hr />
|
||||
<button id="back">Back</button>
|
||||
<hr />
|
||||
</div>
|
||||
<script>
|
||||
let blobUrl = null
|
||||
|
||||
document.querySelector('#file').onchange = evt => {
|
||||
if (evt.target.files.length > 0) {
|
||||
const reader = new FileReader()
|
||||
|
||||
reader.onload = readerEvent => {
|
||||
document.querySelector('textarea').value = readerEvent.target.result
|
||||
document.querySelector('#file').value = ''
|
||||
}
|
||||
|
||||
reader.readAsText(evt.target.files[0])
|
||||
}
|
||||
}
|
||||
|
||||
for (const voice of ['awb', 'kal', 'kal16', 'rms', 'slt']) {
|
||||
document.querySelector(`#${voice}`).onclick = () => {
|
||||
const text = document.querySelector('textarea').value
|
||||
|
||||
if (text.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
document.querySelector('#div1').style.setProperty('display', 'none')
|
||||
|
||||
fetch('/', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([voice, text])
|
||||
})
|
||||
.then(r => {
|
||||
if (r.ok) {
|
||||
r.blob()
|
||||
.then(blob => {
|
||||
document.querySelector(
|
||||
'#durationSpan'
|
||||
).textContent = `Duration: ${r.headers.get('Duration')}`
|
||||
|
||||
blobUrl = URL.createObjectURL(blob)
|
||||
|
||||
const a = document.createElement('a')
|
||||
a.innerText = 'Save'
|
||||
a.href = blobUrl
|
||||
switch (r.headers.get('Content-Type')) {
|
||||
case 'audio/wav':
|
||||
a.download = 'file.wav'
|
||||
break
|
||||
case 'audio/flac':
|
||||
a.download = 'file.flac'
|
||||
break
|
||||
default:
|
||||
a.download = 'file'
|
||||
}
|
||||
|
||||
const audio = document.createElement('audio')
|
||||
audio.src = blobUrl
|
||||
audio.controls = 'controls'
|
||||
|
||||
document.querySelector('#saveSpan').appendChild(a)
|
||||
document.querySelector('#audioSpan').appendChild(audio)
|
||||
|
||||
document
|
||||
.querySelector('#div2')
|
||||
.style.setProperty('display', 'grid')
|
||||
})
|
||||
.catch(() => {
|
||||
document
|
||||
.querySelector('#div1')
|
||||
.style.setProperty('display', 'grid')
|
||||
})
|
||||
} else {
|
||||
document
|
||||
.querySelector('#div1')
|
||||
.style.setProperty('display', 'grid')
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
document
|
||||
.querySelector('#div1')
|
||||
.style.setProperty('display', 'grid')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('#back').onclick = () => {
|
||||
document.querySelector('#div2').style.setProperty('display', 'none')
|
||||
|
||||
document.querySelector('#durationSpan').textContent = ''
|
||||
|
||||
document
|
||||
.querySelector('#saveSpan')
|
||||
.removeChild(document.querySelector('#saveSpan a'))
|
||||
|
||||
document
|
||||
.querySelector('#audioSpan')
|
||||
.removeChild(document.querySelector('#audioSpan audio'))
|
||||
|
||||
URL.revokeObjectURL(blobUrl)
|
||||
blobUrl = null
|
||||
|
||||
document.querySelector('#div1').style.setProperty('display', 'grid')
|
||||
}
|
||||
|
||||
if (CSS.supports('color-scheme', 'dark only')) {
|
||||
document.querySelector('#forceDarkMode').disabled = false
|
||||
|
||||
document.querySelector('#forceDarkMode').checked =
|
||||
'darkmode' in document.documentElement.dataset &&
|
||||
document.documentElement.dataset.darkmode === 'f'
|
||||
|
||||
document.querySelector('#forceDarkMode').onclick = () => {
|
||||
if (location.hash === '#forceDarkMode=0') {
|
||||
location.href = location.href.substring(
|
||||
0,
|
||||
location.href.indexOf('#')
|
||||
)
|
||||
} else {
|
||||
location.hash = '#forceDarkMode=0'
|
||||
location.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Alexia Rose
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
Loading…
Reference in New Issue