[+] refactor book1

1. move book1 into a private repo;
This commit is contained in:
Siarhei Siniak 2025-01-12 18:20:19 +03:00
parent 584b4b652f
commit 136b5709b0
8 changed files with 3 additions and 5472 deletions

2
.gitmodules vendored

@ -21,4 +21,4 @@
url = https://gitea.fxreader.online/nartes/com.github.aiortc.aiortc
[submodule "deps/online.fxreader.nartes.books"]
path = deps/online.fxreader.nartes.books
url = https://gitea.fxreader.online/nartes/books
url = ssh://gitea@127.0.0.1:19000/nartes/books.git

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -1,78 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<script
src="https://code.jquery.com/jquery-3.6.0.slim.min.js"
integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI="
crossorigin="anonymous"
></script>
<title>Speech synthesiser</title>
<script>
window.context = {};
window.context.loading = {};
window.context.books = [];
</script>
<script src="NoSleep.min.js"></script>
<script src="script.js"></script>
<script src="book.js"></script>
<link rel="stylesheet" href="style.css">
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div class=voice-settings>
<h1>Speech synthesiser</h1>
<p>Enter some text in the input below and press return or the "play" button to hear it. change voices using the dropdown menu.</p>
<form>
<input type="text" class="txt">
<div>
<label for="rate">Rate</label><input type="range" min="0.5" max="2" value="1" step="0.1" id="rate">
<div class="rate-value">1</div>
<div class="clearfix"></div>
</div>
<div>
<label for="pitch">Pitch</label><input type="range" min="0" max="2" value="1" step="0.1" id="pitch">
<div class="pitch-value">1</div>
<div class="clearfix"></div>
</div>
<select class=voice-select>
</select>
<div class="controls">
<button id="play" type="submit">Play</button>
</div>
</form>
</div>
<div class=screen>
<div class=widget>
<select name=book>
<!--<option value=0>Death of a Hear</option>-->
</select>
<br/>
<span>Current Sentence: </span>
<input type=input name=current-sentence></input>
<span>Total Sentences: </span>
<input type=input name=total-sentences disabled>
</input>
<br/>
<input type=button name=add-book value="Add Book">
<input type=button name=read-aloud value="Read Aloud">
<input type=button name=debug value="Debug">
</input>
<br/>
</div>
<pre class=status>
</pre>
</div>
</body>
</html>

@ -1,550 +0,0 @@
class Future {
constructor() {
let self = this;
self.promise = new Promise(
(resolve, reject) => {
self.resolve = resolve;
self.reject = reject;
}
);
}
}
context.loading.script = new Future();
$(window).on('load', async () => {
var synth = window.speechSynthesis;
var inputForm = document.querySelector('form');
var inputTxt = document.querySelector('.txt');
var voiceSelect = document.querySelector('select');
var pitch = document.querySelector('#pitch');
var pitchValue = document.querySelector('.pitch-value');
var rate = document.querySelector('#rate');
var rateValue = document.querySelector('.rate-value');
var voices = [];
context.nosleep_timer = null;
context.ui = {
voice_settings_div: $('.voice-settings'),
voice_select: $('.voice-select'),
status_pre: $('.status'),
books_select: $('.screen .widget select[name=book]'),
current_sentence_input:
$('.screen .widget input[name=current-sentence]'),
total_sentences_input:
$('.screen .widget input[name=total-sentences]'),
read_aloud:
$('.screen .widget input[name=read-aloud]'),
add_book:
$('.screen .widget input[name=add-book]'),
debug:
$('.screen .widget input[name=debug]'),
};
context.update_books = () => {
context.ui.books_select.empty();
window.context.books.map(
(o, i) =>
$('<option>')
.attr('value', '' + i)
.attr('url', o.url || '')
.text(o.text.slice(0, 10))
).forEach((o) => context.ui.books_select.append(o))
}
context.update_books();
context.sentences = null;
context.pending_stop = false;
context.current_book = null;
context.nosleep = new NoSleep();
context.is_debug = false;
context.log = {
error: [],
info: [],
};
context.callbacks = {
log_error: (msg) => {
if (context.is_debug)
{
console.error(msg);
context.log.error.push(msg);
}
},
enable_no_sleep: () => {
if (context.nosleep_timer != null)
{
context.callbacks.log_error('running already');
}
context.nosleep_timer = setInterval(
() => {
location.hash = 'nosleep' + Math.random();
context.callbacks.update_status();
/*
if ('vibrate' in window.navigator)
{
window.navigator.vibrate(200);
}
*/
}, 1000
);
},
get_state: () => {
let t1 = localStorage['state'];
if (t1)
{
return JSON.parse(t1);
}
else
{
return {};
}
},
get_cookie: (key) => {
/*
return document.cookie.split('; ').map(
(o) => o.split('=')
).reduce(
(b, a) => {
if (a.length == 2) {b[a[0]] = a[1]};
return b
},
{}
)[key];
*/
let t1 = localStorage['state'];
if (t1 != undefined)
{
let t2 = JSON.parse(t1);
return t2[key];
}
else
{
return undefined;
}
},
set_cookie: (key, value) => {
let state = context.callbacks.get_state('state');
state[key] = value;
//document.cookie = `${key}=${value};`;
localStorage['state'] = JSON.stringify(state);
context.callbacks.update_status();
},
disable_no_sleep: () => {
if (context.nosleep_timer == null)
{
context.callbacks.log_error('nothing is running');
}
clearInterval(context.nosleep_timer);
location.hash = '';
context.nosleep_timer = null;
synth.cancel();
},
continuous_reading: async() => {
if (context.is_reading)
{
context.pending_stop = true;
return;
}
context.is_reading = true;
context.nosleep.enable();
context.callbacks.enable_no_sleep();
context.ui.voice_settings_div.addClass('hidden');
context.ui.current_sentence_input.attr(
'disabled',
'disabled'
);
while (
context.callbacks.get_cookie('sentence_id') < context.sentences.length &&
!context.pending_stop
)
{
let sentence =
context.sentences[context.callbacks.get_cookie('sentence_id')];
//context.callbacks.log_error('start');
try {
await context.read_aloud(
context.sentences[
context.callbacks.get_cookie('sentence_id')
]
);
} catch (e) {
context.callbacks.log_error(e);
}
//context.callbacks.log_error('finished');
if (!context.pending_stop)
{
context.callbacks.set_cookie(
'sentence_id',
context.callbacks.get_cookie('sentence_id') + 1
);
}
}
context.pending_stop = false;
context.ui.current_sentence_input.removeAttr('disabled');
context.nosleep.disable();
context.ui.voice_settings_div.removeClass('hidden');
context.callbacks.disable_no_sleep();
context.is_reading = false;
},
update_status: () => {
let data = {};
data.state = context.callbacks.get_state();
if (
context.callbacks.get_cookie('sentence_id') != null &&
context.sentences != null &&
context.callbacks.get_cookie('sentence_id') < context.sentences.length
)
{
data.sentence = context.sentences[context.callbacks.get_cookie('sentence_id')];
}
data.pending_stop = context.pending_stop;
data.is_reading = context.is_reading;
data.log = context.log;
context.ui.current_sentence_input.val(
context.callbacks.get_cookie('sentence_id')
);
data.timestamp = (new Date());
data.version = 'v0.1.7';
data.speech_synthesis = {
paused: synth.paused,
pending: synth.pending,
speaking: synth.speaking,
};
/*
if (!synth.speaking && context.is_reading)
{
synth.cancel();
}
*/
context.ui.status_pre.text(
JSON.stringify(
data,
null,
4,
)
);
},
ui_read_aloud_on_click: async() => {
let book_id = parseInt(context.ui.books_select.val());
if (context.current_book != book_id)
{
if (context.books[book_id].url)
{
context.callbacks.set_cookie(
'book_url',
context.books[book_id].url,
);
}
context.current_book = book_id;
context.sentences =
context.books[
context.current_book
].text.replaceAll(/([\.\?\!])\s+/g,'$1\n')
.split('\n');
context.ui.total_sentences_input.val(
context.sentences.length,
);
{
let state = context.callbacks.get_state();
}
}
if (
context.ui.current_sentence_input.val() != ''
)
{
try{
let sentence_id = parseInt(
context.ui.current_sentence_input.val()
);
if (
sentence_id >= 0 &&
sentence_id < context.sentences.length
)
{
context.callbacks.set_cookie(
'sentence_id',
sentence_id
);
}
} catch (e) {
context.callbacks.log_error(e);
}
}
if (context.is_reading && !context.pending_stop)
{
context.pending_stop = true;
}
else
{
context.callbacks.continuous_reading();
}
},
book_add: async (url) => {
let book = await (
(await fetch(url)).text()
);
//let book = prompt('enter text', '');
//let title = prompt('enter title', '');
//window.context.books.push(title + '\n' + book);
window.context.books.push({
text: book, url
});
window.context.update_books();
},
populateVoiceList: () => {
voices = synth.getVoices().sort(function (a, b) {
const aname = a.name.toUpperCase(), bname = b.name.toUpperCase();
if ( aname < bname ) return -1;
else if ( aname == bname ) return 0;
else return +1;
});
//var selectedIndex = voiceSelect.selectedIndex < 0 ? 0 : voiceSelect.selectedIndex;
voiceSelect.innerHTML = '';
for(i = 0; i < voices.length ; i++) {
var option = document.createElement('option');
option.textContent = voices[i].name + ' (' + voices[i].lang + ')';
if(voices[i].default) {
option.textContent += ' -- DEFAULT';
}
{
let voice = context.callbacks.get_cookie('voice');
if (voice && option.textContent == voice)
{
$(option).attr('selected', 'selected');
}
}
option.setAttribute('data-lang', voices[i].lang);
option.setAttribute('data-name', voices[i].name);
voiceSelect.appendChild(option);
}
//voiceSelect.selectedIndex = selectedIndex;
},
init: async () => {
let state = context.callbacks.get_state();
context.ui.voice_select.val(state.voice);
if (!state.book_id)
{
context.callbacks.set_cookie(
'book_id',
0,
);
}
if (!state.sentence_id)
{
context.callbacks.set_cookie(
'sentence_id',
0,
);
}
if (state.book_url)
{
await context.callbacks.book_add(state.book_url);
state.book_id = context.books.length - 1;
}
if (state.book_id)
{
context.ui.books_select.find(
'>option',
).eq(state.book_id).attr('selected', 'selected');
}
if (state.sentence_id)
{
context.ui.current_sentence_input.val(
state.sentence_id,
);
}
},
};
context.callbacks.populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = context.callbacks.populateVoiceList;
}
await context.callbacks.init();
context.ui.add_book.on(
'click',
async () => {
// alert('fuck');
let url = 'books/' + prompt('enter book file', '1.txt');
await context.callbacks.book_add(url);
},
);
context.ui.read_aloud.on(
'click',
context.callbacks.ui_read_aloud_on_click,
);
context.ui.voice_select.on(
'change',
() => {
context.callbacks.set_cookie(
'voice',
context.ui.voice_select.val()
);
}
);
context.ui.debug.on(
'click',
() => {
if (context.is_debug)
{
context.is_debug = false;
}
else
{
context.is_debug = true;
}
context.callbacks.update_status();
}
);
context.read_aloud = async (raw_line) => {
line = raw_line.trim();
if (line.length == 0)
{
return;
}
let sleep_detect = null;
let exit = () => {
if (sleep_detect != null)
{
clearInterval(sleep_detect);
}
}
return new Promise((response, reject) => {
if (synth.speaking) {
context.callbacks.log_error('speechSynthesis.speaking');
if (reject != undefined)
{
reject('error');
}
return;
}
let utterThis = new SpeechSynthesisUtterance(line);
utterThis.onend = function (event) {
exit();
context.callbacks.log_error(
'SpeechSynthesisUtterance.onend ' + event.error
);
if (response != undefined)
{
response('done ' + event.error);
}
}
utterThis.onpause = function (event) {
exit();
context.callbacks.log_error('SpeechSynthesisUtterance.onpause');
if (reject != undefined)
{
reject('paused ' + event.error);
}
}
utterThis.onerror = function (event) {
exit();
context.callbacks.log_error(
'SpeechSynthesisUtterance.onerror ' + event.error
);
if (reject != undefined)
{
reject('error ' + event.error);
}
}
let selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
for(i = 0; i < voices.length ; i++) {
if(voices[i].name === selectedOption) {
utterThis.voice = voices[i];
break;
}
}
//window.alert('fuck3');
utterThis.pitch = pitch.value;
utterThis.rate = rate.value;
synth.speak(utterThis);
let silence_count = 0;
sleep_detect = setInterval(
() => {
if (!synth.speaking)
{
context.callbacks.log_error(
'silence count is ' + silence_count
)
++silence_count;
}
if (silence_count == 3 || context.pending_stop)
{
exit();
if (context.pending_stop)
{
synth.cancel();
reject('pending stop');
}
else
{
context.callbacks.log_error('phone is sleeping, retry');
response('utterance is not present');
}
/*
context.read_aloud(
line
).then(response).catch(reject);
*/
}
},
100,
);
});
}
function speak(){
let line = inputTxt.value;
if (line !== '') {
context.read_aloud(line);
}
}
inputForm.onsubmit = function(event) {
event.preventDefault();
speak();
inputTxt.blur();
}
pitch.onchange = function() {
pitchValue.textContent = pitch.value;
}
rate.onchange = function() {
rateValue.textContent = rate.value;
}
voiceSelect.onchange = function(){
speak();
}
context.loading.script.resolve(true);
});

@ -1,84 +0,0 @@
body, html {
margin: 0;
}
html {
height: 100%;
}
body {
height: 90%;
max-width: 800px;
margin: 0 auto;
}
h1, p {
font-family: sans-serif;
text-align: center;
padding: 20px;
}
.txt, select, form > div {
display: block;
margin: 0 auto;
font-family: sans-serif;
font-size: 16px;
padding: 5px;
}
.txt {
width: 80%;
}
select {
width: 83%;
}
form > div {
width: 81%;
}
.txt, form > div {
margin-bottom: 10px;
overflow: auto;
}
.clearfix {
clear: both;
}
label {
float: left;
width: 10%;
line-height: 1.5;
}
.rate-value, .pitch-value {
float: right;
width: 5%;
line-height: 1.5;
}
#rate, #pitch {
float: right;
width: 81%;
}
.controls {
text-align: center;
margin-top: 10px;
}
.controls button {
padding: 10px;
}
.hidden
{
display: none !important;
}
pre {
word-break: break-all;
white-space: pre-wrap;
}

@ -1 +1 @@
Subproject commit f94ded8fbfe62bea6e96827312eb25853990ecd3
Subproject commit e27d400c07ddbe66e1ef73da1a5754c28cfec157

1
deps/online.fxreader.nartes.books vendored Submodule

@ -0,0 +1 @@
Subproject commit 7cc59533c0eddbe04e3b58109d1d861a6f92f40d