Spaces:
Sleeping
Sleeping
j
commited on
Commit
·
170e15c
1
Parent(s):
4fa523b
update from base repository
Browse files- Dockerfile +10 -32
- app/faster_whisper/core.py +1 -0
- app/run.py +36 -6
- app/webservice.py +5 -1
- reascripts/ReaSpeech/source/ColumnLayout.lua +70 -0
- reascripts/ReaSpeech/source/ReaSpeechAPI.lua +10 -3
- reascripts/ReaSpeech/source/ReaSpeechActionsUI.lua +6 -2
- reascripts/ReaSpeech/source/ReaSpeechControlsUI.lua +252 -125
- reascripts/ReaSpeech/source/ReaSpeechWorker.lua +25 -9
- reascripts/ReaSpeech/source/Theme.lua +5 -2
- reascripts/ReaSpeech/tests/TestColumnLayout.lua +202 -0
- reascripts/ReaSpeech/tests/TestReaSpeechUI.lua +1 -0
Dockerfile
CHANGED
@@ -1,14 +1,8 @@
|
|
1 |
FROM swaggerapi/swagger-ui:v4.18.2 AS swagger-ui
|
2 |
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
|
3 |
|
4 |
-
ARG SERVICE_USER=service
|
5 |
-
ARG SERVICE_UID=1001
|
6 |
-
ARG SERVICE_GID=1001
|
7 |
-
|
8 |
ENV PYTHON_VERSION=3.10
|
9 |
ENV POETRY_VENV=/app/.venv
|
10 |
-
ENV HF_HOME="/app/.cache"
|
11 |
-
ENV ASR_MODEL_PATH="/app/.cache"
|
12 |
|
13 |
RUN export DEBIAN_FRONTEND=noninteractive \
|
14 |
&& apt-get -qq update \
|
@@ -29,44 +23,28 @@ RUN ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python3 && \
|
|
29 |
ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python && \
|
30 |
ln -s -f /usr/bin/pip3 /usr/bin/pip
|
31 |
|
32 |
-
RUN
|
33 |
-
|
34 |
-
|
35 |
-
RUN getent group $SERVICE_USER
|
36 |
-
RUN getent passwd $SERVICE_USER
|
37 |
-
|
38 |
-
COPY --chown=$SERVICE_UID:$SERVICE_GID . /app
|
39 |
-
COPY --chown=$SERVICE_UID:$SERVICE_GID --from=swagger-ui /usr/share/nginx/html/swagger-ui.css /app/swagger-ui-assets/swagger-ui.css
|
40 |
-
COPY --chown=$SERVICE_UID:$SERVICE_GID --from=swagger-ui /usr/share/nginx/html/swagger-ui-bundle.js /app/swagger-ui-assets/swagger-ui-bundle.js
|
41 |
-
|
42 |
-
RUN chown -R $SERVICE_UID:$SERVICE_GID /app
|
43 |
|
44 |
-
|
45 |
-
RUN mkdir -p /app/.cache && chown -R $SERVICE_UID:$SERVICE_GID /app/.cache && ls -la /app/.cache
|
46 |
-
|
47 |
-
USER $SERVICE_USER
|
48 |
|
49 |
WORKDIR /app
|
50 |
|
51 |
-
|
52 |
-
$POETRY_VENV/bin/pip install poetry==1.6.1
|
53 |
-
|
54 |
-
ENV PATH="${PATH}:${POETRY_VENV}/bin"
|
55 |
-
|
56 |
-
COPY --chown=$SERVICE_UID:$SERVICE_GID poetry.lock pyproject.toml ./
|
57 |
|
58 |
RUN poetry config virtualenvs.in-project true
|
59 |
RUN poetry install --no-root
|
60 |
|
61 |
-
|
|
|
|
|
|
|
|
|
62 |
RUN $POETRY_VENV/bin/pip install --no-cache-dir torch==1.13.1+cu117 -f https://download.pytorch.org/whl/torch
|
63 |
|
64 |
WORKDIR /app/reascripts/ReaSpeech
|
65 |
-
|
66 |
-
RUN ls -la /app/reascripts/ReaSpeech
|
67 |
-
|
68 |
RUN make publish
|
69 |
-
|
70 |
WORKDIR /app
|
71 |
RUN rm -rf reascripts
|
72 |
|
|
|
1 |
FROM swaggerapi/swagger-ui:v4.18.2 AS swagger-ui
|
2 |
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
|
3 |
|
|
|
|
|
|
|
|
|
4 |
ENV PYTHON_VERSION=3.10
|
5 |
ENV POETRY_VENV=/app/.venv
|
|
|
|
|
6 |
|
7 |
RUN export DEBIAN_FRONTEND=noninteractive \
|
8 |
&& apt-get -qq update \
|
|
|
23 |
ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python && \
|
24 |
ln -s -f /usr/bin/pip3 /usr/bin/pip
|
25 |
|
26 |
+
RUN python3 -m venv $POETRY_VENV \
|
27 |
+
&& $POETRY_VENV/bin/pip install -U pip setuptools \
|
28 |
+
&& $POETRY_VENV/bin/pip install poetry==1.6.1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
ENV PATH="${PATH}:${POETRY_VENV}/bin"
|
|
|
|
|
|
|
31 |
|
32 |
WORKDIR /app
|
33 |
|
34 |
+
COPY poetry.lock pyproject.toml ./
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
RUN poetry config virtualenvs.in-project true
|
37 |
RUN poetry install --no-root
|
38 |
|
39 |
+
COPY . .
|
40 |
+
COPY --from=swagger-ui /usr/share/nginx/html/swagger-ui.css swagger-ui-assets/swagger-ui.css
|
41 |
+
COPY --from=swagger-ui /usr/share/nginx/html/swagger-ui-bundle.js swagger-ui-assets/swagger-ui-bundle.js
|
42 |
+
|
43 |
+
RUN poetry install && rm -rf /root/.cache/pypoetry
|
44 |
RUN $POETRY_VENV/bin/pip install --no-cache-dir torch==1.13.1+cu117 -f https://download.pytorch.org/whl/torch
|
45 |
|
46 |
WORKDIR /app/reascripts/ReaSpeech
|
|
|
|
|
|
|
47 |
RUN make publish
|
|
|
48 |
WORKDIR /app
|
49 |
RUN rm -rf reascripts
|
50 |
|
app/faster_whisper/core.py
CHANGED
@@ -13,6 +13,7 @@ from .utils import ResultWriter, WriteTXT, WriteSRT, WriteVTT, WriteTSV, WriteJS
|
|
13 |
ASR_ENGINE_OPTIONS = frozenset([
|
14 |
"task",
|
15 |
"language",
|
|
|
16 |
"initial_prompt",
|
17 |
"vad_filter",
|
18 |
"word_timestamps",
|
|
|
13 |
ASR_ENGINE_OPTIONS = frozenset([
|
14 |
"task",
|
15 |
"language",
|
16 |
+
"hotwords",
|
17 |
"initial_prompt",
|
18 |
"vad_filter",
|
19 |
"word_timestamps",
|
app/run.py
CHANGED
@@ -30,6 +30,9 @@ argmap = {
|
|
30 |
'--asr-model': {
|
31 |
'default': os.getenv('ASR_MODEL', 'small'),
|
32 |
'help': 'ASR model to use (default: %(default)s)' },
|
|
|
|
|
|
|
33 |
}
|
34 |
|
35 |
parser = argparse.ArgumentParser()
|
@@ -46,26 +49,53 @@ os.environ['FFMPEG_BIN'] = args.ffmpeg_bin
|
|
46 |
os.environ['ASR_ENGINE'] = args.asr_engine
|
47 |
os.environ['ASR_MODEL'] = args.asr_model
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
# Start Redis
|
50 |
print('Starting database...', file=sys.stderr)
|
51 |
-
subprocess.Popen([args.redis_bin], stdout=subprocess.DEVNULL)
|
52 |
|
53 |
# Start Celery
|
54 |
print('Starting worker...', file=sys.stderr)
|
55 |
-
subprocess.Popen(['celery', '-A', 'app.worker.celery', 'worker', '--pool=solo', '--loglevel=info'])
|
56 |
|
57 |
# Start Gunicorn
|
58 |
print('Starting application...', file=sys.stderr)
|
59 |
-
subprocess.Popen(['gunicorn', '--bind', '0.0.0.0:9000', '--workers', '1', '--timeout', '0', 'app.webservice:app', '-k', 'uvicorn.workers.UvicornWorker'])
|
60 |
|
61 |
# Wait for any process to exit
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
|
65 |
# Terminate any child processes
|
66 |
print('Terminating child processes...', file=sys.stderr)
|
67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
# Exit with status of process that exited
|
|
|
70 |
print('Exiting with status', status, file=sys.stderr)
|
71 |
sys.exit(status)
|
|
|
30 |
'--asr-model': {
|
31 |
'default': os.getenv('ASR_MODEL', 'small'),
|
32 |
'help': 'ASR model to use (default: %(default)s)' },
|
33 |
+
'--build-reascripts': {
|
34 |
+
'action': 'store_true',
|
35 |
+
'help': 'Build ReaScripts before starting' },
|
36 |
}
|
37 |
|
38 |
parser = argparse.ArgumentParser()
|
|
|
49 |
os.environ['ASR_ENGINE'] = args.asr_engine
|
50 |
os.environ['ASR_MODEL'] = args.asr_model
|
51 |
|
52 |
+
if args.build_reascripts:
|
53 |
+
if os.system('cd reascripts/ReaSpeech && make') != 0:
|
54 |
+
print('ReaScript build failed', file=sys.stderr)
|
55 |
+
sys.exit(1)
|
56 |
+
|
57 |
+
processes = {}
|
58 |
+
|
59 |
# Start Redis
|
60 |
print('Starting database...', file=sys.stderr)
|
61 |
+
processes['redis'] = subprocess.Popen([args.redis_bin], stdout=subprocess.DEVNULL)
|
62 |
|
63 |
# Start Celery
|
64 |
print('Starting worker...', file=sys.stderr)
|
65 |
+
processes['celery'] = subprocess.Popen(['celery', '-A', 'app.worker.celery', 'worker', '--pool=solo', '--loglevel=info'])
|
66 |
|
67 |
# Start Gunicorn
|
68 |
print('Starting application...', file=sys.stderr)
|
69 |
+
processes['gunicorn'] = subprocess.Popen(['gunicorn', '--bind', '0.0.0.0:9000', '--workers', '1', '--timeout', '0', 'app.webservice:app', '-k', 'uvicorn.workers.UvicornWorker'])
|
70 |
|
71 |
# Wait for any process to exit
|
72 |
+
pid, waitstatus = os.wait()
|
73 |
+
exitcode = os.waitstatus_to_exitcode(waitstatus)
|
74 |
+
process_name = '<unknown>'
|
75 |
+
for name, p in processes.items():
|
76 |
+
if p.pid == pid:
|
77 |
+
process_name = name
|
78 |
+
break
|
79 |
+
if exitcode < 0:
|
80 |
+
print('Process', process_name, 'received signal', -exitcode, file=sys.stderr)
|
81 |
+
else:
|
82 |
+
print('Process', process_name, 'exited with status', exitcode, file=sys.stderr)
|
83 |
|
84 |
# Terminate any child processes
|
85 |
print('Terminating child processes...', file=sys.stderr)
|
86 |
+
for name, p in processes.items():
|
87 |
+
try:
|
88 |
+
print('Terminating', name, file=sys.stderr)
|
89 |
+
|
90 |
+
# kinda bass-ackwards, but poll() returns None if process is still running
|
91 |
+
if not p.poll():
|
92 |
+
p.terminate()
|
93 |
+
else:
|
94 |
+
print(name, "already exited", file=sys.stderr)
|
95 |
+
except Exception as e:
|
96 |
+
print(e, file=sys.stderr)
|
97 |
|
98 |
# Exit with status of process that exited
|
99 |
+
status = 1 if exitcode < 0 else exitcode
|
100 |
print('Exiting with status', status, file=sys.stderr)
|
101 |
sys.exit(status)
|
app/webservice.py
CHANGED
@@ -24,6 +24,7 @@ ASR_ENGINE = os.getenv("ASR_ENGINE", "faster_whisper")
|
|
24 |
ASR_OPTIONS = frozenset([
|
25 |
"task",
|
26 |
"language",
|
|
|
27 |
"initial_prompt",
|
28 |
"encode",
|
29 |
"output",
|
@@ -36,6 +37,8 @@ DEFAULT_MODEL_NAME = os.getenv("ASR_MODEL", "small")
|
|
36 |
|
37 |
LANGUAGE_CODES = sorted(list(tokenizer.LANGUAGES.keys()))
|
38 |
|
|
|
|
|
39 |
projectMetadata = importlib.metadata.metadata('reaspeech')
|
40 |
app = FastAPI(
|
41 |
# docs_url=None,
|
@@ -111,6 +114,7 @@ async def reascript(request: Request, name: str, host: str):
|
|
111 |
async def asr(
|
112 |
task: Union[str, None] = Query(default="transcribe", enum=["transcribe", "translate"]),
|
113 |
language: Union[str, None] = Query(default=None, enum=LANGUAGE_CODES),
|
|
|
114 |
initial_prompt: Union[str, None] = Query(default=None),
|
115 |
audio_file: UploadFile = File(...),
|
116 |
encode: bool = Query(default=True, description="Encode audio first through ffmpeg"),
|
@@ -137,7 +141,7 @@ async def asr(
|
|
137 |
transcriber = transcribe.si(temp_file_path, audio_file.filename, asr_options)
|
138 |
|
139 |
if use_async:
|
140 |
-
job = transcriber.apply_async()
|
141 |
return JSONResponse({"job_id": job.id})
|
142 |
|
143 |
else:
|
|
|
24 |
ASR_OPTIONS = frozenset([
|
25 |
"task",
|
26 |
"language",
|
27 |
+
"hotwords",
|
28 |
"initial_prompt",
|
29 |
"encode",
|
30 |
"output",
|
|
|
37 |
|
38 |
LANGUAGE_CODES = sorted(list(tokenizer.LANGUAGES.keys()))
|
39 |
|
40 |
+
TASK_EXPIRATION_SECONDS = 30
|
41 |
+
|
42 |
projectMetadata = importlib.metadata.metadata('reaspeech')
|
43 |
app = FastAPI(
|
44 |
# docs_url=None,
|
|
|
114 |
async def asr(
|
115 |
task: Union[str, None] = Query(default="transcribe", enum=["transcribe", "translate"]),
|
116 |
language: Union[str, None] = Query(default=None, enum=LANGUAGE_CODES),
|
117 |
+
hotwords: Union[str, None] = Query(default=None),
|
118 |
initial_prompt: Union[str, None] = Query(default=None),
|
119 |
audio_file: UploadFile = File(...),
|
120 |
encode: bool = Query(default=True, description="Encode audio first through ffmpeg"),
|
|
|
141 |
transcriber = transcribe.si(temp_file_path, audio_file.filename, asr_options)
|
142 |
|
143 |
if use_async:
|
144 |
+
job = transcriber.apply_async(expires=TASK_EXPIRATION_SECONDS)
|
145 |
return JSONResponse({"job_id": job.id})
|
146 |
|
147 |
else:
|
reascripts/ReaSpeech/source/ColumnLayout.lua
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
--[[
|
2 |
+
|
3 |
+
ColumnLayout.lua - Fixed-width column layout helper
|
4 |
+
|
5 |
+
]]--
|
6 |
+
|
7 |
+
ColumnLayout = Polo {
|
8 |
+
DEFAULT_COLUMN_PADDING = 15,
|
9 |
+
DEFAULT_NUM_COLUMNS = 3,
|
10 |
+
}
|
11 |
+
|
12 |
+
function ColumnLayout:init()
|
13 |
+
assert(self.render_column, 'render_column function must be provided')
|
14 |
+
self.column_padding = self.column_padding or self.DEFAULT_COLUMN_PADDING
|
15 |
+
self.margin_bottom = self.margin_bottom or 0
|
16 |
+
self.margin_left = self.margin_left or 0
|
17 |
+
self.margin_right = self.margin_right or 0
|
18 |
+
self.margin_top = self.margin_top or 0
|
19 |
+
self.num_columns = self.num_columns or self.DEFAULT_NUM_COLUMNS
|
20 |
+
self.width = self.width or 0
|
21 |
+
end
|
22 |
+
|
23 |
+
function ColumnLayout:render()
|
24 |
+
local total_padding = (self.num_columns - 1) * self.column_padding
|
25 |
+
local total_width = self.width
|
26 |
+
if total_width == 0 then
|
27 |
+
total_width = self:_get_avail_width()
|
28 |
+
end
|
29 |
+
local content_width = total_width - self.margin_left - self.margin_right
|
30 |
+
local column_width = (content_width - total_padding) / self.num_columns
|
31 |
+
|
32 |
+
self:_horiz_margin(self.margin_left)
|
33 |
+
|
34 |
+
for i = 1, self.num_columns do
|
35 |
+
local column = {num = i, width = column_width}
|
36 |
+
|
37 |
+
self:_with_group(function ()
|
38 |
+
self:_vert_margin(self.margin_top, column_width)
|
39 |
+
self.render_column(column)
|
40 |
+
self:_vert_margin(self.margin_bottom, column_width)
|
41 |
+
end)
|
42 |
+
|
43 |
+
if i < self.num_columns then
|
44 |
+
self:_column_gap(self.column_padding)
|
45 |
+
end
|
46 |
+
end
|
47 |
+
end
|
48 |
+
|
49 |
+
function ColumnLayout:_column_gap(padding)
|
50 |
+
ImGui.SameLine(ctx, 0, padding)
|
51 |
+
end
|
52 |
+
|
53 |
+
function ColumnLayout:_get_avail_width()
|
54 |
+
local avail_width, _ = ImGui.GetContentRegionAvail(ctx)
|
55 |
+
return avail_width
|
56 |
+
end
|
57 |
+
|
58 |
+
function ColumnLayout:_horiz_margin(margin)
|
59 |
+
ImGui.SetCursorPosX(ctx, ImGui.GetCursorPosX(ctx) + margin)
|
60 |
+
end
|
61 |
+
|
62 |
+
function ColumnLayout:_vert_margin(margin, width)
|
63 |
+
ImGui.Dummy(ctx, width, margin)
|
64 |
+
end
|
65 |
+
|
66 |
+
function ColumnLayout:_with_group(f)
|
67 |
+
ImGui.BeginGroup(ctx)
|
68 |
+
app:trap(f)
|
69 |
+
ImGui.EndGroup(ctx)
|
70 |
+
end
|
reascripts/ReaSpeech/source/ReaSpeechAPI.lua
CHANGED
@@ -30,8 +30,10 @@ end
|
|
30 |
|
31 |
-- Fetch simple JSON responses. Will block until result or curl timeout.
|
32 |
-- For large amounts of data, use fetch_large instead.
|
33 |
-
function ReaSpeechAPI:fetch_json(url_path, http_method, error_handler)
|
34 |
http_method = http_method or 'GET'
|
|
|
|
|
35 |
|
36 |
local curl = self:get_curl_cmd()
|
37 |
local api_url = self:get_api_url(url_path)
|
@@ -63,8 +65,13 @@ function ReaSpeechAPI:fetch_json(url_path, http_method, error_handler)
|
|
63 |
end
|
64 |
|
65 |
local status, output = exec_result:match("(%d+)\n(.*)")
|
|
|
66 |
|
67 |
-
if
|
|
|
|
|
|
|
|
|
68 |
local msg = "Curl failed with status " .. status
|
69 |
app:debug(msg)
|
70 |
error_handler(msg)
|
@@ -190,7 +197,7 @@ function ReaSpeechAPI.http_status_and_body(response)
|
|
190 |
|
191 |
local status = last_status_line:match("^HTTP/%d%.%d%s+(%d+)")
|
192 |
if not status then
|
193 |
-
return -1, '
|
194 |
end
|
195 |
|
196 |
local body = {}
|
|
|
30 |
|
31 |
-- Fetch simple JSON responses. Will block until result or curl timeout.
|
32 |
-- For large amounts of data, use fetch_large instead.
|
33 |
+
function ReaSpeechAPI:fetch_json(url_path, http_method, error_handler, timeout_handler)
|
34 |
http_method = http_method or 'GET'
|
35 |
+
error_handler = error_handler or function(_msg) end
|
36 |
+
timeout_handler = timeout_handler or function() end
|
37 |
|
38 |
local curl = self:get_curl_cmd()
|
39 |
local api_url = self:get_api_url(url_path)
|
|
|
65 |
end
|
66 |
|
67 |
local status, output = exec_result:match("(%d+)\n(.*)")
|
68 |
+
status = tonumber(status)
|
69 |
|
70 |
+
if status == 28 then
|
71 |
+
app:debug("Curl timeout reached")
|
72 |
+
timeout_handler()
|
73 |
+
return nil
|
74 |
+
elseif status ~= 0 then
|
75 |
local msg = "Curl failed with status " .. status
|
76 |
app:debug(msg)
|
77 |
error_handler(msg)
|
|
|
197 |
|
198 |
local status = last_status_line:match("^HTTP/%d%.%d%s+(%d+)")
|
199 |
if not status then
|
200 |
+
return -1, 'Status not found in headers'
|
201 |
end
|
202 |
|
203 |
local body = {}
|
reascripts/ReaSpeech/source/ReaSpeechActionsUI.lua
CHANGED
@@ -60,9 +60,13 @@ function ReaSpeechActionsUI:render()
|
|
60 |
end
|
61 |
|
62 |
ImGui.SameLine(ctx)
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
64 |
end
|
65 |
-
ImGui.Dummy(ctx,0, 5)
|
66 |
end
|
67 |
|
68 |
function ReaSpeechActionsUI.make_job(media_item, take)
|
|
|
60 |
end
|
61 |
|
62 |
ImGui.SameLine(ctx)
|
63 |
+
local overlay = string.format("%.0f%%", progress * 100)
|
64 |
+
local status = self.worker:status()
|
65 |
+
if status then
|
66 |
+
overlay = overlay .. ' - ' .. status
|
67 |
+
end
|
68 |
+
ImGui.ProgressBar(ctx, progress, nil, nil, overlay)
|
69 |
end
|
|
|
70 |
end
|
71 |
|
72 |
function ReaSpeechActionsUI.make_job(media_item, take)
|
reascripts/ReaSpeech/source/ReaSpeechControlsUI.lua
CHANGED
@@ -7,178 +7,305 @@ ReaSpeechControlsUI.lua - UI elements for configuring ASR services
|
|
7 |
ReaSpeechControlsUI = Polo {
|
8 |
-- Copied from whisper.tokenizer.LANGUAGES
|
9 |
LANGUAGES = {
|
10 |
-
en = '
|
11 |
-
es = '
|
12 |
-
fr = '
|
13 |
-
tr = '
|
14 |
-
nl = '
|
15 |
-
it = '
|
16 |
-
fi = '
|
17 |
-
uk = '
|
18 |
-
cs = '
|
19 |
-
hu = '
|
20 |
-
th = '
|
21 |
-
bg = '
|
22 |
-
mi = '
|
23 |
-
sk = '
|
24 |
-
lv = '
|
25 |
-
az = '
|
26 |
-
et = '
|
27 |
-
eu = '
|
28 |
-
ne = '
|
29 |
-
kk = '
|
30 |
-
gl = '
|
31 |
-
si = '
|
32 |
-
yo = '
|
33 |
-
oc = '
|
34 |
-
tg = '
|
35 |
-
am = '
|
36 |
-
uz = '
|
37 |
-
ps = '
|
38 |
-
mt = '
|
39 |
-
my = '
|
40 |
-
mg = '
|
41 |
-
haw = '
|
42 |
-
ba = '
|
43 |
},
|
|
|
44 |
LANGUAGE_CODES = {},
|
45 |
-
DEFAULT_LANGUAGE = 'en',
|
46 |
|
47 |
-
|
48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
function ReaSpeechControlsUI:init()
|
|
|
|
|
52 |
self.log_enable = false
|
53 |
self.log_debug = false
|
54 |
|
55 |
self.language = self.DEFAULT_LANGUAGE
|
56 |
self.translate = false
|
|
|
57 |
self.initial_prompt = ''
|
58 |
-
self.model_name =
|
|
|
|
|
|
|
59 |
end
|
60 |
|
61 |
function ReaSpeechControlsUI:get_request_data()
|
62 |
return {
|
63 |
language = self.language,
|
64 |
translate = self.translate,
|
|
|
65 |
initial_prompt = self.initial_prompt,
|
66 |
model_name = self.model_name,
|
|
|
67 |
}
|
68 |
end
|
69 |
|
70 |
-
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
end
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
|
|
|
|
|
|
78 |
|
79 |
-
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
end
|
82 |
|
83 |
-
ReaSpeechControlsUI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
function ReaSpeechControlsUI:render()
|
86 |
-
|
87 |
-
if
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
ImGui.TableSetupColumn(ctx, 'Inputs', ImGui.TableColumnFlags_WidthFixed())
|
92 |
-
-- first column
|
93 |
-
ImGui.TableNextColumn(ctx)
|
94 |
-
ImGui.SameLine(ctx, -10)
|
95 |
-
app.png_from_bytes('reaspeech-logo-small')
|
96 |
-
-- second column
|
97 |
-
ImGui.TableNextColumn(ctx)
|
98 |
-
-- start language selection
|
99 |
-
self:render_language_controls()
|
100 |
-
ImGui.Dummy(ctx,0, 10)
|
101 |
-
self:render_advanced_controls()
|
102 |
-
end)
|
103 |
-
ImGui.EndTable(ctx)
|
104 |
end
|
105 |
-
|
106 |
-
ImGui.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
app.png_from_bytes('heading-logo-tech-audio')
|
|
|
|
|
108 |
end
|
109 |
|
110 |
-
function ReaSpeechControlsUI:
|
111 |
-
if ImGui.
|
112 |
-
app:trap(function()
|
113 |
-
ImGui.
|
114 |
-
|
115 |
-
|
116 |
-
app:trap(function()
|
117 |
-
local combo_items = self.LANGUAGE_CODES
|
118 |
-
for _, combo_item in pairs(combo_items) do
|
119 |
-
local is_selected = (combo_item == self.language)
|
120 |
-
if ImGui.Selectable(ctx, self.LANGUAGES[combo_item], is_selected) then
|
121 |
-
self.language = combo_item
|
122 |
-
end
|
123 |
-
end
|
124 |
end)
|
125 |
-
ImGui.
|
126 |
end
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
end
|
133 |
end)
|
134 |
-
|
135 |
-
ImGui.TreePop(ctx)
|
136 |
end
|
137 |
end
|
138 |
|
139 |
-
function ReaSpeechControlsUI:
|
140 |
-
|
|
|
141 |
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
ImGui.PushItemWidth(ctx, self.LARGE_ITEM_WIDTH)
|
148 |
-
app:trap(function ()
|
149 |
-
rv, value = ImGui.InputText(ctx, 'initial prompt', self.initial_prompt)
|
150 |
-
if rv then
|
151 |
-
self.initial_prompt = value
|
152 |
-
end
|
153 |
-
end)
|
154 |
-
ImGui.PopItemWidth(ctx)
|
155 |
-
|
156 |
-
ImGui.SameLine(ctx)
|
157 |
-
ImGui.PushItemWidth(ctx, 100)
|
158 |
-
app:trap(function ()
|
159 |
-
rv, value = ImGui.InputTextWithHint(ctx, 'model name', self.model_name or "<default>")
|
160 |
-
if rv then
|
161 |
-
self.model_name = value
|
162 |
-
end
|
163 |
-
end)
|
164 |
-
ImGui.PopItemWidth(ctx)
|
165 |
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
|
|
|
|
171 |
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
|
|
|
|
177 |
end
|
178 |
end
|
179 |
end)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
|
181 |
-
|
182 |
-
|
|
|
183 |
end
|
184 |
end
|
|
|
7 |
ReaSpeechControlsUI = Polo {
|
8 |
-- Copied from whisper.tokenizer.LANGUAGES
|
9 |
LANGUAGES = {
|
10 |
+
en = 'English', zh = 'Chinese', de = 'German',
|
11 |
+
es = 'Spanish', ru = 'Russian', ko = 'Korean',
|
12 |
+
fr = 'French', ja = 'Japanese', pt = 'Portuguese',
|
13 |
+
tr = 'Turkish', pl = 'Polish', ca = 'Catalan',
|
14 |
+
nl = 'Dutch', ar = 'Arabic', sv = 'Swedish',
|
15 |
+
it = 'Italian', id = 'Indonesian', hi = 'Hindi',
|
16 |
+
fi = 'Finnish', vi = 'Vietnamese', he = 'Hebrew',
|
17 |
+
uk = 'Ukrainian', el = 'Greek', ms = 'Malay',
|
18 |
+
cs = 'Czech', ro = 'Romanian', da = 'Danish',
|
19 |
+
hu = 'Hungarian', ta = 'Tamil', no = 'Norwegian',
|
20 |
+
th = 'Thai', ur = 'Urdu', hr = 'Croatian',
|
21 |
+
bg = 'Bulgarian', lt = 'Lithuanian', la = 'Latin',
|
22 |
+
mi = 'Maori', ml = 'Malayalam', cy = 'Welsh',
|
23 |
+
sk = 'Slovak', te = 'Telugu', fa = 'Persian',
|
24 |
+
lv = 'Latvian', bn = 'Bengali', sr = 'Serbian',
|
25 |
+
az = 'Azerbaijani', sl = 'Slovenian', kn = 'Kannada',
|
26 |
+
et = 'Estonian', mk = 'Macedonian', br = 'Breton',
|
27 |
+
eu = 'Basque', is = 'Icelandic', hy = 'Armenian',
|
28 |
+
ne = 'Nepali', mn = 'Mongolian', bs = 'Bosnian',
|
29 |
+
kk = 'Kazakh', sq = 'Albanian', sw = 'Swahili',
|
30 |
+
gl = 'Galician', mr = 'Marathi', pa = 'Punjabi',
|
31 |
+
si = 'Sinhala', km = 'Khmer', sn = 'Shona',
|
32 |
+
yo = 'Yoruba', so = 'Somali', af = 'Afrikaans',
|
33 |
+
oc = 'Occitan', ka = 'Georgian', be = 'Belarusian',
|
34 |
+
tg = 'Tajik', sd = 'Sindhi', gu = 'Gujarati',
|
35 |
+
am = 'Amharic', yi = 'Yiddish', lo = 'Lao',
|
36 |
+
uz = 'Uzbek', fo = 'Faroese', ht = 'Haitian Creole',
|
37 |
+
ps = 'Pashto', tk = 'Turkmen', nn = 'Nynorsk',
|
38 |
+
mt = 'Maltese', sa = 'Sanskrit', lb = 'Luxembourgish',
|
39 |
+
my = 'Myanmar', bo = 'Tibetan', tl = 'Tagalog',
|
40 |
+
mg = 'Malagasy', as = 'Assamese', tt = 'Tatar',
|
41 |
+
haw = 'Hawaiian', ln = 'Lingala', ha = 'Hausa',
|
42 |
+
ba = 'Bashkir', jw = 'Javanese', su = 'Sundanese'
|
43 |
},
|
44 |
+
|
45 |
LANGUAGE_CODES = {},
|
|
|
46 |
|
47 |
+
DEFAULT_LANGUAGE = '',
|
48 |
+
DEFAULT_MODEL_NAME = 'small',
|
49 |
+
|
50 |
+
SIMPLE_MODEL_SIZES = {
|
51 |
+
{'Small', 'small'},
|
52 |
+
{'Medium', 'medium'},
|
53 |
+
{'Large', 'distil-large-v3'},
|
54 |
+
},
|
55 |
+
|
56 |
+
COLUMN_PADDING = 15,
|
57 |
+
MARGIN_BOTTOM = 5,
|
58 |
+
MARGIN_LEFT = 115,
|
59 |
+
MARGIN_RIGHT = 0,
|
60 |
+
NARROW_COLUMN_WIDTH = 150,
|
61 |
}
|
62 |
|
63 |
+
ReaSpeechControlsUI._init_languages = function ()
|
64 |
+
for code, _ in pairs(ReaSpeechControlsUI.LANGUAGES) do
|
65 |
+
table.insert(ReaSpeechControlsUI.LANGUAGE_CODES, code)
|
66 |
+
end
|
67 |
+
|
68 |
+
table.sort(ReaSpeechControlsUI.LANGUAGE_CODES, function (a, b)
|
69 |
+
return ReaSpeechControlsUI.LANGUAGES[a] < ReaSpeechControlsUI.LANGUAGES[b]
|
70 |
+
end)
|
71 |
+
|
72 |
+
table.insert(ReaSpeechControlsUI.LANGUAGE_CODES, 1, '')
|
73 |
+
ReaSpeechControlsUI.LANGUAGES[''] = 'Detect'
|
74 |
+
end
|
75 |
+
|
76 |
+
ReaSpeechControlsUI._init_languages()
|
77 |
+
|
78 |
function ReaSpeechControlsUI:init()
|
79 |
+
self.tab = 'simple'
|
80 |
+
|
81 |
self.log_enable = false
|
82 |
self.log_debug = false
|
83 |
|
84 |
self.language = self.DEFAULT_LANGUAGE
|
85 |
self.translate = false
|
86 |
+
self.hotwords = ''
|
87 |
self.initial_prompt = ''
|
88 |
+
self.model_name = self.DEFAULT_MODEL_NAME
|
89 |
+
self.vad_filter = true
|
90 |
+
|
91 |
+
self:init_layouts()
|
92 |
end
|
93 |
|
94 |
function ReaSpeechControlsUI:get_request_data()
|
95 |
return {
|
96 |
language = self.language,
|
97 |
translate = self.translate,
|
98 |
+
hotwords = self.hotwords,
|
99 |
initial_prompt = self.initial_prompt,
|
100 |
model_name = self.model_name,
|
101 |
+
vad_filter = self.vad_filter,
|
102 |
}
|
103 |
end
|
104 |
|
105 |
+
function ReaSpeechControlsUI:init_layouts()
|
106 |
+
self:init_simple_layouts()
|
107 |
+
self:init_advanced_layouts()
|
108 |
+
end
|
109 |
+
|
110 |
+
function ReaSpeechControlsUI:init_simple_layouts()
|
111 |
+
local with_button_color = function (selected, f)
|
112 |
+
if selected then
|
113 |
+
ImGui.PushStyleColor(ctx, ImGui.Col_Button(), Theme.colors.dark_gray_translucent)
|
114 |
+
app:trap(f)
|
115 |
+
ImGui.PopStyleColor(ctx)
|
116 |
+
else
|
117 |
+
f()
|
118 |
+
end
|
119 |
end
|
120 |
|
121 |
+
self.model_sizes_layout = ColumnLayout.new {
|
122 |
+
column_padding = self.COLUMN_PADDING,
|
123 |
+
margin_bottom = self.MARGIN_BOTTOM,
|
124 |
+
margin_left = self.MARGIN_LEFT,
|
125 |
+
margin_right = self.MARGIN_RIGHT,
|
126 |
+
num_columns = #self.SIMPLE_MODEL_SIZES,
|
127 |
|
128 |
+
render_column = function (column)
|
129 |
+
self:render_input_label(column.num == 1 and 'Model Size' or '')
|
130 |
+
local label, model_name = table.unpack(self.SIMPLE_MODEL_SIZES[column.num])
|
131 |
+
with_button_color(self.model_name == model_name, function ()
|
132 |
+
if ImGui.Button(ctx, label, column.width) then
|
133 |
+
self.model_name = model_name
|
134 |
+
end
|
135 |
+
end)
|
136 |
+
end
|
137 |
+
}
|
138 |
end
|
139 |
|
140 |
+
function ReaSpeechControlsUI:init_advanced_layouts()
|
141 |
+
local renderers = {
|
142 |
+
{self.render_model_name, self.render_hotwords, self.render_language},
|
143 |
+
{self.render_options, self.render_initial_prompt, self.render_logging},
|
144 |
+
}
|
145 |
+
|
146 |
+
self.advanced_layouts = {}
|
147 |
+
|
148 |
+
for row = 1, #renderers do
|
149 |
+
self.advanced_layouts[row] = ColumnLayout.new {
|
150 |
+
column_padding = self.COLUMN_PADDING,
|
151 |
+
margin_bottom = self.MARGIN_BOTTOM,
|
152 |
+
margin_left = self.MARGIN_LEFT,
|
153 |
+
margin_right = self.MARGIN_RIGHT,
|
154 |
+
num_columns = #renderers[row],
|
155 |
+
|
156 |
+
render_column = function (column)
|
157 |
+
ImGui.PushItemWidth(ctx, column.width)
|
158 |
+
app:trap(function () renderers[row][column.num](self, column) end)
|
159 |
+
ImGui.PopItemWidth(ctx)
|
160 |
+
end
|
161 |
+
}
|
162 |
+
end
|
163 |
+
end
|
164 |
|
165 |
function ReaSpeechControlsUI:render()
|
166 |
+
self:render_heading()
|
167 |
+
if self.tab == 'advanced' then
|
168 |
+
self:render_advanced()
|
169 |
+
else
|
170 |
+
self:render_simple()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
end
|
172 |
+
ImGui.Separator(ctx)
|
173 |
+
ImGui.Dummy(ctx, 0, 5)
|
174 |
+
end
|
175 |
+
|
176 |
+
function ReaSpeechControlsUI:render_heading()
|
177 |
+
local init_x, init_y = ImGui.GetCursorPos(ctx)
|
178 |
+
|
179 |
+
ImGui.SetCursorPosX(ctx, init_x - 20)
|
180 |
+
app.png_from_bytes('reaspeech-logo-small')
|
181 |
+
|
182 |
+
ImGui.SetCursorPos(ctx, init_x + self.MARGIN_LEFT + 2, init_y)
|
183 |
+
self:render_tabs()
|
184 |
+
|
185 |
+
ImGui.SetCursorPos(ctx, ImGui.GetWindowWidth(ctx) - 55, init_y)
|
186 |
app.png_from_bytes('heading-logo-tech-audio')
|
187 |
+
|
188 |
+
ImGui.SetCursorPos(ctx, init_x, init_y + 40)
|
189 |
end
|
190 |
|
191 |
+
function ReaSpeechControlsUI:render_tabs()
|
192 |
+
if ImGui.BeginTabBar(ctx, '##tabs', ImGui.TabBarFlags_None()) then
|
193 |
+
app:trap(function ()
|
194 |
+
if ImGui.BeginTabItem(ctx, 'Simple') then
|
195 |
+
app:trap(function ()
|
196 |
+
self.tab = 'simple'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
end)
|
198 |
+
ImGui.EndTabItem(ctx)
|
199 |
end
|
200 |
+
if ImGui.BeginTabItem(ctx, 'Advanced') then
|
201 |
+
app:trap(function ()
|
202 |
+
self.tab = 'advanced'
|
203 |
+
end)
|
204 |
+
ImGui.EndTabItem(ctx)
|
205 |
end
|
206 |
end)
|
207 |
+
ImGui.EndTabBar(ctx)
|
|
|
208 |
end
|
209 |
end
|
210 |
|
211 |
+
function ReaSpeechControlsUI:render_simple()
|
212 |
+
self:render_model_sizes()
|
213 |
+
end
|
214 |
|
215 |
+
function ReaSpeechControlsUI:render_advanced()
|
216 |
+
for row = 1, #self.advanced_layouts do
|
217 |
+
self.advanced_layouts[row]:render()
|
218 |
+
end
|
219 |
+
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
|
221 |
+
function ReaSpeechControlsUI:render_input_label(text)
|
222 |
+
ImGui.Text(ctx, text)
|
223 |
+
ImGui.Dummy(ctx, 0, 0)
|
224 |
+
end
|
225 |
+
|
226 |
+
function ReaSpeechControlsUI:render_language(column)
|
227 |
+
self:render_input_label('Language')
|
228 |
|
229 |
+
if ImGui.BeginCombo(ctx, "##language", self.LANGUAGES[self.language]) then
|
230 |
+
app:trap(function()
|
231 |
+
local combo_items = self.LANGUAGE_CODES
|
232 |
+
for _, combo_item in pairs(combo_items) do
|
233 |
+
local is_selected = (combo_item == self.language)
|
234 |
+
if ImGui.Selectable(ctx, self.LANGUAGES[combo_item], is_selected) then
|
235 |
+
self.language = combo_item
|
236 |
end
|
237 |
end
|
238 |
end)
|
239 |
+
ImGui.EndCombo(ctx)
|
240 |
+
end
|
241 |
+
|
242 |
+
local translate_label = "Translate to English"
|
243 |
+
if column.width < self.NARROW_COLUMN_WIDTH then
|
244 |
+
translate_label = "Translate"
|
245 |
+
end
|
246 |
+
local rv, value = ImGui.Checkbox(ctx, translate_label, self.translate)
|
247 |
+
if rv then
|
248 |
+
self.translate = value
|
249 |
+
end
|
250 |
+
end
|
251 |
+
|
252 |
+
function ReaSpeechControlsUI:render_model_name()
|
253 |
+
self:render_input_label('Model Name')
|
254 |
+
|
255 |
+
local rv, value = ImGui.InputTextWithHint(ctx, '##model_name', self.model_name or "<default>")
|
256 |
+
if rv then
|
257 |
+
self.model_name = value
|
258 |
+
end
|
259 |
+
end
|
260 |
+
|
261 |
+
function ReaSpeechControlsUI:render_model_sizes()
|
262 |
+
self.model_sizes_layout:render()
|
263 |
+
end
|
264 |
+
|
265 |
+
function ReaSpeechControlsUI:render_hotwords()
|
266 |
+
self:render_input_label('Hot Words')
|
267 |
+
|
268 |
+
local rv, value = ImGui.InputText(ctx, '##hotwords', self.hotwords)
|
269 |
+
if rv then
|
270 |
+
self.hotwords = value
|
271 |
+
end
|
272 |
+
end
|
273 |
+
|
274 |
+
function ReaSpeechControlsUI:render_options(column)
|
275 |
+
self:render_input_label('Options')
|
276 |
+
|
277 |
+
local vad_label = "Voice Activity Detection"
|
278 |
+
if column.width < self.NARROW_COLUMN_WIDTH then
|
279 |
+
vad_label = "VAD"
|
280 |
+
end
|
281 |
+
local rv, value = ImGui.Checkbox(ctx, vad_label, self.vad_filter)
|
282 |
+
if rv then
|
283 |
+
self.vad_filter = value
|
284 |
+
end
|
285 |
+
end
|
286 |
+
|
287 |
+
function ReaSpeechControlsUI:render_logging()
|
288 |
+
self:render_input_label('Logging')
|
289 |
+
|
290 |
+
local rv, value = ImGui.Checkbox(ctx, "Enable", self.log_enable)
|
291 |
+
if rv then
|
292 |
+
self.log_enable = value
|
293 |
+
end
|
294 |
+
|
295 |
+
if self.log_enable then
|
296 |
+
ImGui.SameLine(ctx)
|
297 |
+
rv, value = ImGui.Checkbox(ctx, "Debug", self.log_debug)
|
298 |
+
if rv then
|
299 |
+
self.log_debug = value
|
300 |
+
end
|
301 |
+
end
|
302 |
+
end
|
303 |
+
|
304 |
+
function ReaSpeechControlsUI:render_initial_prompt()
|
305 |
+
self:render_input_label('Initial Prompt')
|
306 |
|
307 |
+
rv, value = ImGui.InputText(ctx, '##initial_prompt', self.initial_prompt)
|
308 |
+
if rv then
|
309 |
+
self.initial_prompt = value
|
310 |
end
|
311 |
end
|
reascripts/ReaSpeech/source/ReaSpeechWorker.lua
CHANGED
@@ -89,6 +89,12 @@ function ReaSpeechWorker:progress()
|
|
89 |
return completed_job_count / job_count
|
90 |
end
|
91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
function ReaSpeechWorker:cancel()
|
93 |
if self.active_job then
|
94 |
if self.active_job.job and self.active_job.job.job_id then
|
@@ -123,7 +129,7 @@ function ReaSpeechWorker:handle_request(request)
|
|
123 |
task = request.translate and 'translate' or 'transcribe',
|
124 |
output = 'json',
|
125 |
use_async = 'true',
|
126 |
-
vad_filter = 'true',
|
127 |
word_timestamps = 'true',
|
128 |
model_name = request.model_name,
|
129 |
}
|
@@ -132,6 +138,10 @@ function ReaSpeechWorker:handle_request(request)
|
|
132 |
data.language = request.language
|
133 |
end
|
134 |
|
|
|
|
|
|
|
|
|
135 |
if request.initial_prompt and request.initial_prompt ~= '' then
|
136 |
data.initial_prompt = request.initial_prompt
|
137 |
end
|
@@ -156,6 +166,7 @@ function ReaSpeechWorker:handle_job_status(active_job, response)
|
|
156 |
end
|
157 |
|
158 |
active_job.job.job_id = response.job_id
|
|
|
159 |
|
160 |
if not response.job_status then
|
161 |
return false
|
@@ -252,21 +263,23 @@ function ReaSpeechWorker:handle_response_json(output_file, sentinel_file, succes
|
|
252 |
|
253 |
local f = io.open(output_file, 'r')
|
254 |
if not f then
|
255 |
-
fail_f("Couldn't open
|
|
|
256 |
return
|
257 |
end
|
258 |
|
259 |
local http_status, body = ReaSpeechAPI.http_status_and_body(f)
|
260 |
f:close()
|
261 |
|
262 |
-
if
|
263 |
-
|
264 |
return
|
265 |
end
|
266 |
|
|
|
|
|
|
|
267 |
if http_status ~= 200 then
|
268 |
-
Tempfile:remove(sentinel_file)
|
269 |
-
Tempfile:remove(output_file)
|
270 |
local msg = "Server responded with status " .. http_status
|
271 |
fail_f(msg)
|
272 |
app:log(msg)
|
@@ -274,15 +287,18 @@ function ReaSpeechWorker:handle_response_json(output_file, sentinel_file, succes
|
|
274 |
return
|
275 |
end
|
276 |
|
|
|
|
|
|
|
|
|
|
|
277 |
local response = nil
|
278 |
if app:trap(function ()
|
279 |
response = json.decode(body)
|
280 |
end) then
|
281 |
-
Tempfile:remove(sentinel_file)
|
282 |
-
Tempfile:remove(output_file)
|
283 |
success_f(response)
|
284 |
else
|
285 |
-
|
286 |
end
|
287 |
end
|
288 |
|
|
|
89 |
return completed_job_count / job_count
|
90 |
end
|
91 |
|
92 |
+
function ReaSpeechWorker:status()
|
93 |
+
if self.active_job and self.active_job.job then
|
94 |
+
return self.active_job.job.job_status
|
95 |
+
end
|
96 |
+
end
|
97 |
+
|
98 |
function ReaSpeechWorker:cancel()
|
99 |
if self.active_job then
|
100 |
if self.active_job.job and self.active_job.job.job_id then
|
|
|
129 |
task = request.translate and 'translate' or 'transcribe',
|
130 |
output = 'json',
|
131 |
use_async = 'true',
|
132 |
+
vad_filter = request.vad_filter and 'true' or 'false',
|
133 |
word_timestamps = 'true',
|
134 |
model_name = request.model_name,
|
135 |
}
|
|
|
138 |
data.language = request.language
|
139 |
end
|
140 |
|
141 |
+
if request.hotwords and request.hotwords ~= '' then
|
142 |
+
data.hotwords = request.hotwords
|
143 |
+
end
|
144 |
+
|
145 |
if request.initial_prompt and request.initial_prompt ~= '' then
|
146 |
data.initial_prompt = request.initial_prompt
|
147 |
end
|
|
|
166 |
end
|
167 |
|
168 |
active_job.job.job_id = response.job_id
|
169 |
+
active_job.job.job_status = response.job_status
|
170 |
|
171 |
if not response.job_status then
|
172 |
return false
|
|
|
263 |
|
264 |
local f = io.open(output_file, 'r')
|
265 |
if not f then
|
266 |
+
fail_f("Couldn't open output file: " .. tostring(output_file))
|
267 |
+
Tempfile:remove(sentinel_file)
|
268 |
return
|
269 |
end
|
270 |
|
271 |
local http_status, body = ReaSpeechAPI.http_status_and_body(f)
|
272 |
f:close()
|
273 |
|
274 |
+
if http_status == -1 then
|
275 |
+
app:debug(body .. ", trying again later")
|
276 |
return
|
277 |
end
|
278 |
|
279 |
+
Tempfile:remove(output_file)
|
280 |
+
Tempfile:remove(sentinel_file)
|
281 |
+
|
282 |
if http_status ~= 200 then
|
|
|
|
|
283 |
local msg = "Server responded with status " .. http_status
|
284 |
fail_f(msg)
|
285 |
app:log(msg)
|
|
|
287 |
return
|
288 |
end
|
289 |
|
290 |
+
if #body < 1 then
|
291 |
+
fail_f("Empty response from server")
|
292 |
+
return
|
293 |
+
end
|
294 |
+
|
295 |
local response = nil
|
296 |
if app:trap(function ()
|
297 |
response = json.decode(body)
|
298 |
end) then
|
|
|
|
|
299 |
success_f(response)
|
300 |
else
|
301 |
+
fail_f("Error parsing response JSON")
|
302 |
end
|
303 |
end
|
304 |
|
reascripts/ReaSpeech/source/Theme.lua
CHANGED
@@ -33,7 +33,10 @@ function Theme.init()
|
|
33 |
{ ImGui.Col_CheckMark(), Theme.colors.pink_opaque },
|
34 |
{ ImGui.Col_HeaderHovered(), Theme.colors.dark_gray_semi_opaque },
|
35 |
{ ImGui.Col_HeaderActive(), Theme.colors.dark_gray_semi_transparent },
|
36 |
-
{ ImGui.Col_Header(), Theme.colors.dark_gray_semi_opaque }
|
|
|
|
|
|
|
37 |
},
|
38 |
|
39 |
styles = {
|
@@ -47,4 +50,4 @@ function Theme.init()
|
|
47 |
})
|
48 |
|
49 |
return Theme.theme
|
50 |
-
end
|
|
|
33 |
{ ImGui.Col_CheckMark(), Theme.colors.pink_opaque },
|
34 |
{ ImGui.Col_HeaderHovered(), Theme.colors.dark_gray_semi_opaque },
|
35 |
{ ImGui.Col_HeaderActive(), Theme.colors.dark_gray_semi_transparent },
|
36 |
+
{ ImGui.Col_Header(), Theme.colors.dark_gray_semi_opaque },
|
37 |
+
{ ImGui.Col_Tab(), Theme.colors.dark_gray_opaque },
|
38 |
+
{ ImGui.Col_TabActive(), Theme.colors.medium_gray_opaque },
|
39 |
+
{ ImGui.Col_TabHovered(), Theme.colors.dark_gray_translucent },
|
40 |
},
|
41 |
|
42 |
styles = {
|
|
|
50 |
})
|
51 |
|
52 |
return Theme.theme
|
53 |
+
end
|
reascripts/ReaSpeech/tests/TestColumnLayout.lua
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package.path = '../common/libs/?.lua;../common/vendor/?.lua;' .. package.path
|
2 |
+
|
3 |
+
local lu = require('luaunit')
|
4 |
+
|
5 |
+
require('Polo')
|
6 |
+
|
7 |
+
require('source/ColumnLayout')
|
8 |
+
|
9 |
+
|
10 |
+
|
11 |
+
TestColumnLayout = {
|
12 |
+
AVAIL_WIDTH = 100
|
13 |
+
}
|
14 |
+
|
15 |
+
function TestColumnLayout:stub(layout)
|
16 |
+
layout._column_gap_calls = {}
|
17 |
+
layout._column_gap = function (self, padding)
|
18 |
+
table.insert(self._column_gap_calls, {padding})
|
19 |
+
end
|
20 |
+
|
21 |
+
layout._get_avail_width_calls = 0
|
22 |
+
layout._get_avail_width = function (self)
|
23 |
+
self._get_avail_width_calls = self._get_avail_width_calls + 1
|
24 |
+
return TestColumnLayout.AVAIL_WIDTH
|
25 |
+
end
|
26 |
+
|
27 |
+
layout._horiz_margin_calls = {}
|
28 |
+
layout._horiz_margin = function (self, margin)
|
29 |
+
table.insert(self._horiz_margin_calls, {margin})
|
30 |
+
end
|
31 |
+
|
32 |
+
layout._vert_margin_calls = {}
|
33 |
+
layout._vert_margin = function (self, margin, width)
|
34 |
+
table.insert(self._vert_margin_calls, {margin, width})
|
35 |
+
end
|
36 |
+
|
37 |
+
layout._with_group_calls = 0
|
38 |
+
layout._with_group = function (self, f)
|
39 |
+
self._with_group_calls = self._with_group_calls + 1
|
40 |
+
f()
|
41 |
+
end
|
42 |
+
end
|
43 |
+
|
44 |
+
function TestColumnLayout:testInitDefault()
|
45 |
+
local layout = ColumnLayout.new {
|
46 |
+
render_column = function () end
|
47 |
+
}
|
48 |
+
lu.assertEquals(layout.column_padding, ColumnLayout.DEFAULT_COLUMN_PADDING)
|
49 |
+
lu.assertEquals(layout.margin_bottom, 0)
|
50 |
+
lu.assertEquals(layout.margin_left, 0)
|
51 |
+
lu.assertEquals(layout.margin_right, 0)
|
52 |
+
lu.assertEquals(layout.margin_top, 0)
|
53 |
+
lu.assertEquals(layout.num_columns, ColumnLayout.DEFAULT_NUM_COLUMNS)
|
54 |
+
lu.assertEquals(layout.width, 0)
|
55 |
+
end
|
56 |
+
|
57 |
+
function TestColumnLayout:testRender()
|
58 |
+
local column_padding = 20
|
59 |
+
local margin_bottom = 15
|
60 |
+
local margin_left = 10
|
61 |
+
local margin_right = 30
|
62 |
+
local margin_top = 5
|
63 |
+
local num_columns = 2
|
64 |
+
|
65 |
+
local expected_column_width = (
|
66 |
+
self.AVAIL_WIDTH
|
67 |
+
- margin_left
|
68 |
+
- margin_right
|
69 |
+
- column_padding
|
70 |
+
) / num_columns
|
71 |
+
|
72 |
+
local render_column_calls = {}
|
73 |
+
|
74 |
+
local layout = ColumnLayout.new {
|
75 |
+
column_padding = column_padding,
|
76 |
+
margin_bottom = margin_bottom,
|
77 |
+
margin_left = margin_left,
|
78 |
+
margin_right = margin_right,
|
79 |
+
margin_top = margin_top,
|
80 |
+
num_columns = num_columns,
|
81 |
+
render_column = function (column)
|
82 |
+
table.insert(render_column_calls, {column})
|
83 |
+
end
|
84 |
+
}
|
85 |
+
|
86 |
+
self:stub(layout)
|
87 |
+
layout:render()
|
88 |
+
|
89 |
+
lu.assertEquals(layout._column_gap_calls, {{column_padding}})
|
90 |
+
lu.assertEquals(layout._get_avail_width_calls, 1)
|
91 |
+
lu.assertEquals(layout._horiz_margin_calls, {{margin_left}})
|
92 |
+
lu.assertEquals(layout._vert_margin_calls, {
|
93 |
+
{margin_top, expected_column_width},
|
94 |
+
{margin_bottom, expected_column_width},
|
95 |
+
{margin_top, expected_column_width},
|
96 |
+
{margin_bottom, expected_column_width},
|
97 |
+
})
|
98 |
+
lu.assertEquals(layout._with_group_calls, num_columns)
|
99 |
+
|
100 |
+
lu.assertEquals(render_column_calls, {
|
101 |
+
{{num = 1, width = expected_column_width}},
|
102 |
+
{{num = 2, width = expected_column_width}},
|
103 |
+
})
|
104 |
+
end
|
105 |
+
|
106 |
+
function TestColumnLayout:testRenderWithWidth()
|
107 |
+
local column_padding = 15
|
108 |
+
local margin_bottom = 10
|
109 |
+
local margin_left = 5
|
110 |
+
local margin_right = 20
|
111 |
+
local margin_top = 25
|
112 |
+
local num_columns = 3
|
113 |
+
local width = 205
|
114 |
+
|
115 |
+
local expected_column_width = 50
|
116 |
+
|
117 |
+
local render_column_calls = {}
|
118 |
+
|
119 |
+
local layout = ColumnLayout.new {
|
120 |
+
column_padding = column_padding,
|
121 |
+
margin_bottom = margin_bottom,
|
122 |
+
margin_left = margin_left,
|
123 |
+
margin_right = margin_right,
|
124 |
+
margin_top = margin_top,
|
125 |
+
num_columns = num_columns,
|
126 |
+
width = width,
|
127 |
+
render_column = function (column)
|
128 |
+
table.insert(render_column_calls, {column})
|
129 |
+
end
|
130 |
+
}
|
131 |
+
|
132 |
+
self:stub(layout)
|
133 |
+
layout:render()
|
134 |
+
|
135 |
+
lu.assertEquals(layout._column_gap_calls, {{column_padding}, {column_padding}})
|
136 |
+
lu.assertEquals(layout._get_avail_width_calls, 0)
|
137 |
+
lu.assertEquals(layout._horiz_margin_calls, {{margin_left}})
|
138 |
+
lu.assertEquals(layout._vert_margin_calls, {
|
139 |
+
{margin_top, expected_column_width},
|
140 |
+
{margin_bottom, expected_column_width},
|
141 |
+
{margin_top, expected_column_width},
|
142 |
+
{margin_bottom, expected_column_width},
|
143 |
+
{margin_top, expected_column_width},
|
144 |
+
{margin_bottom, expected_column_width},
|
145 |
+
})
|
146 |
+
lu.assertEquals(layout._with_group_calls, num_columns)
|
147 |
+
|
148 |
+
lu.assertEquals(render_column_calls, {
|
149 |
+
{{num = 1, width = expected_column_width}},
|
150 |
+
{{num = 2, width = expected_column_width}},
|
151 |
+
{{num = 3, width = expected_column_width}},
|
152 |
+
})
|
153 |
+
end
|
154 |
+
|
155 |
+
function TestColumnLayout:testRenderNested()
|
156 |
+
local inner_renders = {}
|
157 |
+
local outer_renders = {}
|
158 |
+
|
159 |
+
local outer_layout = ColumnLayout.new {
|
160 |
+
column_padding = 0,
|
161 |
+
num_columns = 2,
|
162 |
+
width = 60,
|
163 |
+
|
164 |
+
render_column = function (outer_column)
|
165 |
+
table.insert(outer_renders, outer_column)
|
166 |
+
|
167 |
+
local inner_layout = ColumnLayout.new {
|
168 |
+
column_padding = 0,
|
169 |
+
num_columns = 3,
|
170 |
+
width = outer_column.width,
|
171 |
+
|
172 |
+
render_column = function (inner_column)
|
173 |
+
table.insert(inner_renders, inner_column)
|
174 |
+
end
|
175 |
+
}
|
176 |
+
self:stub(inner_layout)
|
177 |
+
|
178 |
+
inner_layout:render()
|
179 |
+
end
|
180 |
+
}
|
181 |
+
self:stub(outer_layout)
|
182 |
+
|
183 |
+
outer_layout:render()
|
184 |
+
|
185 |
+
lu.assertEquals(inner_renders, {
|
186 |
+
{num=1, width=10},
|
187 |
+
{num=2, width=10},
|
188 |
+
{num=3, width=10},
|
189 |
+
{num=1, width=10},
|
190 |
+
{num=2, width=10},
|
191 |
+
{num=3, width=10},
|
192 |
+
})
|
193 |
+
|
194 |
+
lu.assertEquals(outer_renders, {
|
195 |
+
{num=1, width=30},
|
196 |
+
{num=2, width=30},
|
197 |
+
})
|
198 |
+
end
|
199 |
+
|
200 |
+
|
201 |
+
|
202 |
+
os.exit(lu.LuaUnit.run())
|
reascripts/ReaSpeech/tests/TestReaSpeechUI.lua
CHANGED
@@ -8,6 +8,7 @@ require('ReaUtil')
|
|
8 |
require('mock_reaper')
|
9 |
|
10 |
require('source/AlertPopup')
|
|
|
11 |
require('source/ReaSpeechActionsUI')
|
12 |
require('source/ReaSpeechAPI')
|
13 |
require('source/ReaSpeechProductActivation')
|
|
|
8 |
require('mock_reaper')
|
9 |
|
10 |
require('source/AlertPopup')
|
11 |
+
require('source/ColumnLayout')
|
12 |
require('source/ReaSpeechActionsUI')
|
13 |
require('source/ReaSpeechAPI')
|
14 |
require('source/ReaSpeechProductActivation')
|