[mod] alles mögliche verbessert
This commit is contained in:
parent
b571c54195
commit
3497430fa4
13 changed files with 255 additions and 36 deletions
|
@ -2,9 +2,7 @@
|
|||
|
||||
## Zu erledigen
|
||||
|
||||
- asynchrones Erstellen der Audio-Dateien (solange nicht fertig, eine Markierung in der Liste anzeigen)
|
||||
- sanitizing überprüfen
|
||||
- Persistenz mit SQLite
|
||||
- Datum für Dokumente ergänzen
|
||||
- eingebetter Media-Spieler?
|
||||
- für jedes bestehende Dokument soll es Funktionen zum Aufbereiten und Herunderladen geben in den Formaten pdf/odt (pandoc nutzen) und ogg
|
||||
- bei Löschung eines Dokuments auch die zugehörigen Dateien löschen
|
||||
|
|
|
@ -9,7 +9,7 @@ proof-of-concept für Partei-Arbeits-Dokumenten-Verwaltung, welche hörbare Vers
|
|||
|
||||
### Voraussetzungen
|
||||
|
||||
- curl (Debian-Paket-Name: `curl`)
|
||||
- [curl](https://curl.se/docs/manpage.html) (Debian-Paket-Name: `curl`)
|
||||
|
||||
|
||||
### Anweisungen
|
||||
|
@ -33,9 +33,9 @@ proof-of-concept für Partei-Arbeits-Dokumenten-Verwaltung, welche hörbare Vers
|
|||
|
||||
### Voraussetzungen
|
||||
|
||||
- PHP auf Kommandozeile (Debian-Paket-Name: `php-cli`)
|
||||
- ffmpeg (Debian-Paket-Name: `ffmpeg`)
|
||||
- beliebigen Browser
|
||||
- [PHP](https://www.php.net/)-Interpreter für Kommandozeile (Debian-Paket-Name: `php-cli`)
|
||||
- [FFmpeg](https://ffmpeg.org/) (Debian-Paket-Name: `ffmpeg`)
|
||||
- Browser
|
||||
|
||||
|
||||
### Anweisungen
|
||||
|
|
|
@ -6,6 +6,14 @@ require_once('string.php');
|
|||
require_once('cache.php');
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function path_wrapped(string $path) : ?string
|
||||
{
|
||||
return (\file_exists($path) ? $path : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function render(string $template_name, array $arguments) : string
|
||||
|
@ -43,24 +51,36 @@ function navigate(string $target) : void
|
|||
|
||||
/**
|
||||
*/
|
||||
function generate_audio(string $input, string $output_path) : void
|
||||
function generate_audio(
|
||||
string $input_value,
|
||||
string $output_path,
|
||||
?array $options = null
|
||||
) : void
|
||||
{
|
||||
$key = \hash('sha256', $input);
|
||||
$path_wav = \rosavox\helpers\string_\coin(
|
||||
'/tmp/{{key}}.wav',
|
||||
$options = \array_merge(
|
||||
[
|
||||
'blocking' => false,
|
||||
],
|
||||
($options ?? [])
|
||||
);
|
||||
$key = \hash('sha256', $input_value);
|
||||
$input_path = \rosavox\helpers\string_\coin(
|
||||
'/tmp/{{key}}.txt',
|
||||
[
|
||||
'key' => $key,
|
||||
]
|
||||
);
|
||||
$path_ogg = $output_path;
|
||||
\file_put_contents($input_path, $input_value);
|
||||
$command = \rosavox\helpers\string_\coin(
|
||||
'echo "{{input}}" | piper/piper --model piper/voice.onnx --output_file {{path_wav}} && ffmpeg -y -i {{path_wav}} {{path_ogg}} ; rm -f {{path_wav}}',
|
||||
'scripts/generate-audio {{input_path}} {{output_path}}{{suffix}}',
|
||||
[
|
||||
'input' => $input,
|
||||
'path_wav' => $path_wav,
|
||||
'path_ogg' => $path_ogg,
|
||||
'input_path' => $input_path,
|
||||
'output_path' => $output_path,
|
||||
'suffix' => ($options['blocking'] ? '' : ' > /dev/null 2>&1 &'),
|
||||
]
|
||||
);
|
||||
// \unlink($input_path);
|
||||
// \error_log(\sprintf("-- %s\n", $command));
|
||||
\exec($command);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,4 +15,19 @@ function coin(string $template, array $arguments) : string
|
|||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function replace_all(
|
||||
string $string,
|
||||
array $replacements
|
||||
) : string
|
||||
{
|
||||
$result = $string;
|
||||
foreach ($replacement as $replacement_key => $replacement_value) {
|
||||
$result = \str_replace($replacement_key, $replacement_value, $result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -61,17 +61,105 @@ function render_list() : string
|
|||
'docs-list-entry',
|
||||
[
|
||||
'label_read' => \rosavox\helpers\misc\translate('action.read'),
|
||||
'label_hear' => \rosavox\helpers\misc\translate('action.hear'),
|
||||
'value_text' => $entry['value']['title'],
|
||||
'value_link_open' => make_link('edit', ['id' => \sprintf('%u', $entry['id'])]),
|
||||
'value_link_read' => \rosavox\services\doc\readable_path($entry['id']),
|
||||
'value_link_hear' => \rosavox\services\doc\audio_path($entry['id']),
|
||||
'value_name' => \rosavox\helpers\string_\coin(
|
||||
'{{name}}.oga',
|
||||
'area_download_readable' => (
|
||||
(fn ($name, $path) => (
|
||||
(! \file_exists($path))
|
||||
?
|
||||
\rosavox\helpers\misc\render(
|
||||
'state-waiting',
|
||||
[
|
||||
'name' => \rosavox\services\doc\name($entry['id']),
|
||||
'info' => \rosavox\helpers\misc\translate('state.generating'),
|
||||
]
|
||||
)
|
||||
:
|
||||
\rosavox\helpers\misc\render(
|
||||
'download',
|
||||
[
|
||||
'text' => \rosavox\helpers\misc\translate('item.readable.short'),
|
||||
'tooltip' => \rosavox\helpers\string_\coin(
|
||||
'{{item}} {{action}}',
|
||||
[
|
||||
'action' => \rosavox\helpers\misc\translate('action.download'),
|
||||
'item' => \rosavox\helpers\misc\translate('item.readable.long'),
|
||||
]
|
||||
),
|
||||
'value_text' => $entry['value']['title'],
|
||||
'link' => $path,
|
||||
'name' => \rosavox\helpers\string_\coin(
|
||||
'{{name}}.md',
|
||||
[
|
||||
'name' => $name,
|
||||
]
|
||||
),
|
||||
'type' => 'text/markdown',
|
||||
]
|
||||
)
|
||||
)) (
|
||||
\rosavox\services\doc\name($entry['id']),
|
||||
\rosavox\services\doc\readable_path($entry['id'])
|
||||
)
|
||||
),
|
||||
'area_download_audible' => (
|
||||
(fn ($name, $path) => (
|
||||
(! \file_exists($path))
|
||||
?
|
||||
\rosavox\helpers\misc\render(
|
||||
'state-waiting',
|
||||
[
|
||||
'info' => \rosavox\helpers\misc\translate('state.generating'),
|
||||
]
|
||||
)
|
||||
:
|
||||
\rosavox\helpers\misc\render(
|
||||
'download',
|
||||
[
|
||||
'text' => \rosavox\helpers\misc\translate('item.audible.short'),
|
||||
'tooltip' => \rosavox\helpers\string_\coin(
|
||||
'{{item}} {{action}}',
|
||||
[
|
||||
'action' => \rosavox\helpers\misc\translate('action.download'),
|
||||
'item' => \rosavox\helpers\misc\translate('item.audible.long'),
|
||||
]
|
||||
),
|
||||
'link' => $path,
|
||||
'name' => \rosavox\helpers\string_\coin(
|
||||
'{{name}}.oga',
|
||||
[
|
||||
'name' => $name,
|
||||
]
|
||||
),
|
||||
'type' => 'audio/ogg',
|
||||
]
|
||||
)
|
||||
)) (
|
||||
\rosavox\services\doc\name($entry['id']),
|
||||
\rosavox\services\doc\audible_path($entry['id'])
|
||||
)
|
||||
),
|
||||
'area_hear' => (
|
||||
(fn ($name, $path) => (
|
||||
(! \file_exists($path))
|
||||
?
|
||||
\rosavox\helpers\misc\render(
|
||||
'state-waiting',
|
||||
[
|
||||
'info' => \rosavox\helpers\misc\translate('state.generating'),
|
||||
]
|
||||
)
|
||||
:
|
||||
\rosavox\helpers\misc\render(
|
||||
'player',
|
||||
[
|
||||
'source_path' => $path,
|
||||
]
|
||||
)
|
||||
)) (
|
||||
\rosavox\services\doc\name($entry['id']),
|
||||
\rosavox\services\doc\audible_path($entry['id'])
|
||||
)
|
||||
),
|
||||
]
|
||||
),
|
||||
\rosavox\services\doc\list_()
|
||||
|
|
28
source/scripts/generate-audio
Executable file
28
source/scripts/generate-audio
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
## functions
|
||||
|
||||
function syntaxerror
|
||||
{
|
||||
echo "SYNTAX: generate-audio <input-path> <output-path>"
|
||||
}
|
||||
|
||||
|
||||
## args
|
||||
|
||||
if [ $# -ge 1 ] ; then input_path=$1 && shift ; else syntaxerror ; fi
|
||||
if [ $# -ge 1 ] ; then output_path=$1 && shift ; else syntaxerror ; fi
|
||||
|
||||
|
||||
## vars
|
||||
|
||||
key=$(cat "${input_path}" | sha256sum | cut -d ' ' -f 1)
|
||||
wav_path=/tmp/${key}.wav
|
||||
|
||||
|
||||
## exec
|
||||
|
||||
cat ${input_path} | piper/piper --model piper/voice.onnx --output_file ${wav_path} > /dev/null
|
||||
ffmpeg -loglevel error -y -i ${wav_path} -vn ${output_path}
|
||||
rm -f ${wav_path}
|
||||
echo ${output_path}
|
|
@ -25,17 +25,17 @@ function name(int $id) : string
|
|||
|
||||
/**
|
||||
*/
|
||||
function audio_path(int $id) : string
|
||||
function readable_path(int $id) : string
|
||||
{
|
||||
return \sprintf('docs/%s.oga', name($id));
|
||||
return \sprintf('docs/%s.md', name($id));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function readable_path(int $id) : string
|
||||
function audible_path(int $id) : string
|
||||
{
|
||||
return \sprintf('docs/%s.md', name($id));
|
||||
return \sprintf('docs/%s.oga', name($id));
|
||||
}
|
||||
|
||||
|
||||
|
@ -101,7 +101,28 @@ function generate_readable(int $id, $doc) : void
|
|||
|
||||
/**
|
||||
*/
|
||||
function generate_audio(int $id, $doc) : void
|
||||
function remove_readable(
|
||||
int $id
|
||||
) : void
|
||||
{
|
||||
$path = readable_path($id);
|
||||
if (! \file_exists($path))
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
\unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function generate_audible(
|
||||
int $id,
|
||||
$doc
|
||||
) : void
|
||||
{
|
||||
$pause = " .\n";
|
||||
$text = \rosavox\helpers\string_\coin(
|
||||
|
@ -132,11 +153,32 @@ function generate_audio(int $id, $doc) : void
|
|||
);
|
||||
\rosavox\helpers\misc\generate_audio(
|
||||
$text,
|
||||
audio_path($id)
|
||||
audible_path($id),
|
||||
[
|
||||
'blocking' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function remove_audible(
|
||||
int $id
|
||||
) : void
|
||||
{
|
||||
$path = audible_path($id);
|
||||
if (! \file_exists($path))
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
\unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function empty_() : array
|
||||
|
@ -173,7 +215,7 @@ function create(array $doc) : int
|
|||
{
|
||||
$id = state::$storage->create($doc);
|
||||
generate_readable($id, $doc);
|
||||
generate_audio($id, $doc);
|
||||
generate_audible($id, $doc);
|
||||
return $id;
|
||||
}
|
||||
|
||||
|
@ -183,8 +225,10 @@ function create(array $doc) : int
|
|||
function update(int $id, array $doc) : void
|
||||
{
|
||||
state::$storage->update($id, $doc);
|
||||
remove_readable($id);
|
||||
remove_audible($id);
|
||||
generate_readable($id, $doc);
|
||||
generate_audio($id, $doc);
|
||||
generate_audible($id, $doc);
|
||||
}
|
||||
|
||||
|
||||
|
@ -193,6 +237,8 @@ function update(int $id, array $doc) : void
|
|||
function delete(int $id) : void
|
||||
{
|
||||
state::$storage->delete($id);
|
||||
remove_readable($id);
|
||||
remove_audible($id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -217,7 +263,7 @@ function add_examples() : void
|
|||
'authors' => [
|
||||
'Fenriswolf',
|
||||
],
|
||||
'content' => 'Der Allvater hat mich betrogen. Ich werde ihn und seine elende Asenbrut verschlingen.',
|
||||
'content' => 'Der Allvater hat mich betrogen. Ich werde ihn und seine elende Asenbrut verschlingen. Diese Welt wird enden.',
|
||||
'reasoning' => null,
|
||||
]
|
||||
);
|
||||
|
|
|
@ -6,6 +6,12 @@
|
|||
"action.delete": "löschen",
|
||||
"action.hear": "hören",
|
||||
"action.read": "lesen",
|
||||
"action.download": "herunterladen",
|
||||
"item.audible.short": "A",
|
||||
"item.audible.long": "Audio",
|
||||
"item.readable.short": "T",
|
||||
"item.readable.long": "Text",
|
||||
"state.generating": "wird erstellt",
|
||||
"domain.doc.title": "Titel",
|
||||
"domain.doc.authors": "Autoren",
|
||||
"domain.doc.content": "Formulierung",
|
||||
|
|
|
@ -82,7 +82,22 @@ button
|
|||
font-size: 1.0em;
|
||||
}
|
||||
|
||||
.docs-list-entry
|
||||
{
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.docs-list-entry > *
|
||||
{
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.state-waiting
|
||||
{
|
||||
cursor: progress;
|
||||
}
|
||||
|
||||
.download::before
|
||||
{
|
||||
content: "\21A7 ";
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<li class="docs-list-entry">
|
||||
<a href="{{value_link_open}}">{{value_text}}</a>
|
||||
|
|
||||
<a href="{{value_link_read}}">[{{label_read}}]</a>
|
||||
{{area_download_readable}}
|
||||
|
|
||||
<a href="{{value_link_hear}}" download="{{value_name}}" type="audio/ogg">[{{label_hear}}]</a>
|
||||
{{area_download_audible}}
|
||||
|
|
||||
<audio controls="controls" src="{{value_link_hear}}"></audio>
|
||||
{{area_hear}}
|
||||
</li>
|
||||
|
|
1
source/templates/download.html.tpl
Normal file
1
source/templates/download.html.tpl
Normal file
|
@ -0,0 +1 @@
|
|||
<a class="download" href="{{link}}" download="{{name}}" type="{{type}}" title="{{tooltip}}">{{text}}</a>
|
1
source/templates/player.html.tpl
Normal file
1
source/templates/player.html.tpl
Normal file
|
@ -0,0 +1 @@
|
|||
<audio controls="controls" src="{{source_path}}"></audio>
|
1
source/templates/state-waiting.html.tpl
Normal file
1
source/templates/state-waiting.html.tpl
Normal file
|
@ -0,0 +1 @@
|
|||
<span class="state-waiting" title="{{info}}">[~]</span>
|
Loading…
Add table
Reference in a new issue