mirror of
https://github.com/illian64/llm-translate.git
synced 2026-07-09 17:28:38 +00:00
whisper
This commit is contained in:
parent
883a992bc3
commit
07154e93d4
18 changed files with 453 additions and 81 deletions
|
|
@ -4,7 +4,8 @@ from os import walk
|
|||
|
||||
from app import text_splitter, file_processor, dto, log
|
||||
from app.cache import Cache
|
||||
from app.params import TranslationParams, TextSplitParams, TextProcessParams, CacheParams, FileProcessingParams
|
||||
from app.params import TranslationParams, TextSplitParams, TextProcessParams, CacheParams, FileProcessingParams, \
|
||||
RestLogParams
|
||||
from app.text_processor import pre_process
|
||||
from jaa import JaaCore
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ class AppCore(JaaCore):
|
|||
self.text_process_params: TextProcessParams | None = None
|
||||
self.cache_params: CacheParams | None = None
|
||||
self.file_processing_params: FileProcessingParams | None = None
|
||||
self.rest_log_params: RestLogParams | None = None
|
||||
|
||||
self.translators: dict = {}
|
||||
self.initialized_translator_engines: dict[str, dto.TranslatePluginInitInfo] = dict()
|
||||
|
|
@ -42,6 +44,7 @@ class AppCore(JaaCore):
|
|||
init_info.name = cmd
|
||||
init_info.processing_function = manifest["file_processing"][cmd][1]
|
||||
init_info.processed_file_name_function = manifest["file_processing"][cmd][2]
|
||||
init_info.after_processing_function = manifest["file_processing"][cmd][3]
|
||||
logger.info("Init file processing plugin '%s' for next file extensions: %s",
|
||||
init_info.name, init_info.supported_extensions)
|
||||
for ext in init_info.supported_extensions:
|
||||
|
|
@ -163,6 +166,12 @@ class AppCore(JaaCore):
|
|||
else:
|
||||
translate_text = req.text
|
||||
|
||||
# log request / response text
|
||||
if self.rest_log_params is not None and (self.rest_log_params.translate_resp_text or self.rest_log_params.translate_req_text):
|
||||
log_source = "\nSource:\n" + req.text if self.rest_log_params.translate_req_text else ""
|
||||
log_translate = "\nResult:\n" + translate_text if self.rest_log_params.translate_resp_text else ""
|
||||
logger.info(f"Translate text.{log_source}{log_translate}")
|
||||
|
||||
return dto.TranslateResp(result=translate_text, parts=translate_parts, error=None)
|
||||
except ValueError as ve:
|
||||
return dto.TranslateResp(result=None, parts=None, error=ve.args[0])
|
||||
|
|
@ -237,6 +246,11 @@ class AppCore(JaaCore):
|
|||
except Exception as e:
|
||||
log.log_exception("Error proces files: ", e)
|
||||
return dto.ProcessingFileDirResp(files=list(), error=getattr(e, 'message', repr(e)))
|
||||
finally:
|
||||
# clear memory (GPU or RAM) for heavy file processing plugins
|
||||
for processors_list in self.files_ext_to_processors.values():
|
||||
for processor in processors_list:
|
||||
processor.after_processing_function(self)
|
||||
|
||||
def process_file(self, req: dto.ProcessingFileDirReq, root: str, file_name: str) -> dto.ProcessingFileResp:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ class FileProcessingPluginInitInfo:
|
|||
plugin_name: str
|
||||
processing_function: Callable[[Any, ProcessingFileStruct, ProcessingFileDirReq], ProcessingFileResp]
|
||||
processed_file_name_function: Callable[[Any, ProcessingFileStruct, ProcessingFileDirReq], str]
|
||||
after_processing_function: Callable[[Any], None]
|
||||
supported_extensions: set[str] # lower case
|
||||
|
||||
def __init__(self, plugin_name: str, supported_extensions: set[str]):
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ def processed_file_name_def(file_struct: ProcessingFileStruct, req: ProcessingFi
|
|||
return f'{file_struct.file_name}__{from_lang_part}_{req.to_lang}.{file_struct.file_ext}'
|
||||
|
||||
|
||||
def file_name_from_template(file_struct: ProcessingFileStruct, req: ProcessingFileDirReq, options: dict) -> str:
|
||||
def file_name_from_template(file_struct: ProcessingFileStruct, req: ProcessingFileDirReq, options: dict,
|
||||
replace_ext: str = None) -> str:
|
||||
"""
|
||||
Generate output file name from template. Template in options, for preserve original and not.
|
||||
Special parameters in template:
|
||||
|
|
@ -26,14 +27,38 @@ def file_name_from_template(file_struct: ProcessingFileStruct, req: ProcessingFi
|
|||
:param file_struct: struct with file info
|
||||
:param req: file process request
|
||||
:param options: template parameters source
|
||||
:param replace_ext: replace ext, if not None or empty, instead file_struct.file_ext
|
||||
:return: output file name
|
||||
"""
|
||||
file_ext = file_struct.file_ext if replace_ext is None or replace_ext == "" else replace_ext
|
||||
template_dict = options["output_file_name_template"]
|
||||
template = template_dict["preserve_original"] if req.preserve_original_text else template_dict["without_original"]
|
||||
|
||||
return file_name_from_predefined_template(file_struct=file_struct, req=req,
|
||||
template=template, replace_ext=replace_ext)
|
||||
|
||||
|
||||
def file_name_from_predefined_template(file_struct: ProcessingFileStruct, req: ProcessingFileDirReq,
|
||||
template: str, replace_ext: str = None) -> str:
|
||||
"""
|
||||
Generate output file name from template. Template in options, for preserve original and not.
|
||||
Special parameters in template:
|
||||
%%source%% - original file name
|
||||
%%from_lang%% - source language
|
||||
%%to_lang%% - target language
|
||||
|
||||
:param file_struct: struct with file info
|
||||
:param req: file process request
|
||||
:param template: template string
|
||||
:param replace_ext: replace ext, if not None or empty, instead file_struct.file_ext
|
||||
:return: output file name
|
||||
"""
|
||||
file_ext = file_struct.file_ext if replace_ext is None or replace_ext == "" else replace_ext
|
||||
|
||||
return ((template.replace("%%source%%", file_struct.file_name)
|
||||
.replace("%%from_lang%%", req.from_lang)
|
||||
.replace("%%to_lang%%", req.to_lang))
|
||||
+ "." + file_struct.file_ext)
|
||||
.replace("%%from_lang%%", req.from_lang)
|
||||
.replace("%%to_lang%%", req.to_lang))
|
||||
+ "." + file_ext)
|
||||
|
||||
|
||||
def get_file_with_path_for_list(init_dir: str, root: str, file_name: str) -> str:
|
||||
|
|
|
|||
|
|
@ -92,6 +92,13 @@ class FileProcessingTextFormat:
|
|||
return self.translate_prefix + text + self.translate_postfix
|
||||
|
||||
|
||||
@dataclass
|
||||
class RestLogParams:
|
||||
translate_req_text: bool
|
||||
translate_resp_text: bool
|
||||
translate_validation_errors: bool
|
||||
|
||||
|
||||
def read_plugin_translate_params(manifest: dict):
|
||||
manifest["options"]["translation_params_struct"] = read_translation_params(manifest)
|
||||
manifest["options"]["text_split_params_struct"] = read_text_split_params(manifest)
|
||||
|
|
@ -202,4 +209,14 @@ def read_plugin_file_processing_text_format(options: dict):
|
|||
)
|
||||
|
||||
|
||||
def read_rest_log_params(manifest: dict) -> RestLogParams:
|
||||
options = manifest["options"]
|
||||
|
||||
return RestLogParams(
|
||||
translate_req_text=options["rest_log_params"]["translate_req_text"],
|
||||
translate_resp_text=options["rest_log_params"]["translate_resp_text"],
|
||||
translate_validation_errors=options["rest_log_params"]["translate_validation_errors"],
|
||||
)
|
||||
|
||||
|
||||
tp: TranslateProgress = TranslateProgress(unit="part", ascii=True, desc="translate parts: ")
|
||||
|
|
|
|||
|
|
@ -110,6 +110,11 @@
|
|||
* * default_to_lang - двухбуквенный код языка, будет использован, как язык, на который необходимо перевести, если он не был явно указан.
|
||||
* * sleep_after_translate - время в секундах (может быть дробным, например, 0.1), которое севрис будет ожидать после завершения перевода.
|
||||
|
||||
* Группа **rest_log_params** - параметры логирования запросов и ответов перевода.
|
||||
* * translate_req_text - логирование исходного текста
|
||||
* * translate_resp_text - логирование текста перевода
|
||||
* * translate_validation_errors - логирование ошибок в текстах запросов
|
||||
|
||||
* **v** - служебное значение, версия плагина. При повышении версии плагин дополнит файл конфигурации новыми параметрами.
|
||||
|
||||
### Как текст разбивается на части при использовании параметра split_expected_length
|
||||
|
|
|
|||
38
doc/ru/plugins-file-processing/file_media_whisper.md
Normal file
38
doc/ru/plugins-file-processing/file_media_whisper.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Плагин обработки файлов: media
|
||||
|
||||
Часть параметров, общих для всех плагинов, описана [здесь](../processing_files.md).
|
||||
|
||||
## Параметры плагина
|
||||
|
||||
* **model** - модель для распознавания звука. Модели можно увидеть например [здесь](https://github.com/openai/whisper/blob/main/whisper/__init__.py).
|
||||
Вот этот список: tiny.en, tiny, base.en, base, small.en, small, medium.en, medium, large-v1, large-v2, large-v3, large, large-v3-turbo, turbo
|
||||
|
||||
* **cuda** - `true` - использовать видеокарту (быстрее), `false` - использовать cpu (медленнее).
|
||||
|
||||
* **cuda_device_index** - если в системе несколько видеокарт, можно выбрать ту, в которую будет загружена модель.
|
||||
Номер и имя видеокарты указывается при старте приложения в логе, вида `INFO GPU #0: NVIDIA GeForce RTX 4090`.
|
||||
`0` - указываемый в параметре номер.
|
||||
|
||||
* **unload_model_after_processing** - выгружать модель из памяти после завершения обработки списка файлов. Можно использовать, если на карте мало памяти.
|
||||
|
||||
* **translate_after_processing** - переводить субтитры сразу после распознавания. По умолчанию, будет запущен обработчик [srt-Файлов](file_srt.md).
|
||||
|
||||
* **output_file_name_template** - шаблон для имени файла.
|
||||
|
||||
Следующие пункты подробно описаны в документации к Whisper, например здесь https://github.com/openai/whisper/blob/main/whisper/transcribe.py, поэтому ниже общее описание.
|
||||
|
||||
* **temperature** - массив значений точности распознавания.
|
||||
|
||||
* **condition_on_previous_text** - основываться на предыдущем тексте.
|
||||
|
||||
* **word_timestamps** - временные метки для слов - нужно для корректного создания субтитров.
|
||||
|
||||
* **hallucination_silence_threshold** - порог галлюцинаций (додумывания текста) при распознавании.
|
||||
|
||||
* **carry_initial_prompt** - если True, то значение `initial_prompt` будет добавлено в каждый вызов функции декодирования.
|
||||
|
||||
* **initial_prompt** - подсказки дял перевода, например, чтобы помочь распознавать трудные слова.
|
||||
|
||||
* **compression_ratio_threshold** - считать распознавание неудачным при значении выше этого параметра.
|
||||
|
||||
* **logprob_threshold** - если средняя логарифмическая вероятность ниже этого значения, считать распознавание неудачным
|
||||
15
doc/ru/plugins-file-processing/file_srt.md
Normal file
15
doc/ru/plugins-file-processing/file_srt.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Плагин обработки файлов: srt
|
||||
|
||||
Часть параметров, общих для всех плагинов, описана [здесь](../processing_files.md).
|
||||
|
||||
## Параметры плагина
|
||||
|
||||
* **text_format** - параметры форматирования текста в обрабатываемом файле.
|
||||
* * **original_prefix** - символы, которые будут подставлены в начале оригинального текста.
|
||||
* * **original_postfix** - символы, которые будут подставлены в конце оригинального текста.
|
||||
* * **translate_prefix** - символы, которые будут подставлены в начале переведенного текста.
|
||||
* * **translate_postfix** - символы, которые будут подставлены в конце переведенного текста.
|
||||
|
||||
* **remove_src_filename_postfix** - удаление из имени исходного файла постфикса. Постфикс может пригодиться, чтобы выбирать по части имени исходные и переведенные файлы.
|
||||
|
||||
* **translate_delimiter** - разделитель между оригиналом и переводом.
|
||||
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
* **file_txt** - [документация](plugins-file-processing/file_txt.md). Перевод книг в формате txt.
|
||||
|
||||
* **file_media_whisper** - [документация](plugins-file-processing/file_media_whisper.md). Распознавание текста в медиа-файлах. Поддерживаются практически все файлы, которые может прочитать ffmpeg.
|
||||
|
||||
* **file_srt** - [документация](plugins-file-processing/file_txt.md). Перевод субтитров srt. Можно найти существующие субтитры, или распознать звуковую дорожку через плагин `file_media_whisper`.
|
||||
|
||||
## Параметры, которые задаются в основной конфигурации
|
||||
|
||||
Некоторые параметры не настраиваются в плагинах, например, пути для исходных / обработанных файлов.
|
||||
|
|
|
|||
|
|
@ -6,67 +6,6 @@
|
|||
Модули могут работать независимо, используя только функционал сервиса, или вызывать внешние приложения.
|
||||
Параметры, использование моделей и прочее более подробно указано в документации для каждого плагина отдельно.
|
||||
|
||||
## Перевод через REST API
|
||||
|
||||
Сервис поддерживает перевод через REST-запросы. Просмотреть API можно здесь по адресу host:port/docs,
|
||||
по умолчанию здесь: http://127.0.0.1:4990/docs
|
||||
|
||||
Базовый запрос для перевода: POST http://127.0.0.1:4990/translate
|
||||
Минимальное работающее тело запроса выглядит так:
|
||||
```json
|
||||
{
|
||||
"text": "hi"
|
||||
}
|
||||
```
|
||||
Для такого запроса будут автоматически выбраны язык оригинала, язык на который нужно выполнить перевод,
|
||||
плагин для перевода - все это задается через [параметры](options.md).
|
||||
|
||||
Все параметры для перевода можно переопределить вручную.
|
||||
```json
|
||||
{
|
||||
"text": "hi",
|
||||
"context": "context",
|
||||
"from_lang": "en",
|
||||
"to_lang": "ru",
|
||||
"translator_plugin": "lm_studio"
|
||||
}
|
||||
```
|
||||
В этом запросе:
|
||||
* **text** - текст для перевода
|
||||
* **context** - дополнительный контекст. Например, можно указать, чтобы какое-то имя переводилось определенным образом,
|
||||
или дать понять, какого стиля переводимый текст - художественный, научный, юридический и т. д.
|
||||
* **from_lang** - двухбуквенный код языка оригинала. Коды можно посмотреть в файле [common.js](../../static/common.js) проекта.
|
||||
* **to_lang** - двухбуквенный код языка, на который нужно выполнить перевод.
|
||||
* **translator_plugin** - плагин дял перевода. Он должен быть загружен в сервис.
|
||||
|
||||
Также можно выполнить GET запрос и передат ьвсе параметры в него, например
|
||||
http://127.0.0.1:4990/translate?text=hi&from_lang=en&to_lang=ru&context=context&translator_plugin=lm_studio
|
||||
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"result": "Здравствуйте!",
|
||||
"parts": [
|
||||
{
|
||||
"text": "hi",
|
||||
"translate": "Здравствуйте!",
|
||||
"paragraph_end": true
|
||||
}
|
||||
],
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
* **result** - результат перевода.
|
||||
* **parts** - если текст в процессе перевода был разбит на несколько частей, в этом массиве будут представлены
|
||||
все части, и перевод каждой из частей. Это может пригодиться для понимания того, почему перевод был выполнен
|
||||
не полностью и подобрать более подходящие параметры разбиения текста на части.
|
||||
* * **parts[].text** - часть исходного текста, на исходном языке, после преобразований.
|
||||
* * **parts[].translate** - перевод части текста
|
||||
* * **parts[].paragraph_end** является ли часть последним предложением в параграфе (необходимо для соединения частей в поле результата `result`).
|
||||
* **error** - если перевод был выполнен успешно - null, иначе - текст ошибки.
|
||||
|
||||
|
||||
## Список поддерживаемых плагинов
|
||||
|
||||
* **kobold_cpp** - [проект](https://github.com/LostRuins/koboldcpp), [документация](plugins-translate/kobold_cpp.md).
|
||||
|
|
@ -96,4 +35,76 @@ http://127.0.0.1:4990/translate?text=hi&from_lang=en&to_lang=ru&context=context&
|
|||
|
||||
* **no_translate** - этот плагин не является переводчиком, поэтому не имеет параметров.
|
||||
Возвращает в ответе переданный в запросе текст без изменений.
|
||||
Его можно использовать для отладки при работе с параметрами и разработке функционала сервиса.
|
||||
Его можно использовать для отладки при работе с параметрами и разработке функционала сервиса.
|
||||
|
||||
|
||||
## Перевод через REST API
|
||||
|
||||
Сервис поддерживает перевод через REST-запросы. Просмотреть API можно здесь по адресу host:port/docs,
|
||||
по умолчанию здесь: http://127.0.0.1:4990/docs
|
||||
|
||||
Базовый запрос для перевода: POST http://127.0.0.1:4990/translate
|
||||
Минимальное работающее тело запроса выглядит так:
|
||||
```json
|
||||
{
|
||||
"text": "hi"
|
||||
}
|
||||
```
|
||||
Для такого запроса будут автоматически выбраны язык оригинала, язык на который нужно выполнить перевод,
|
||||
плагин для перевода - все это задается через [параметры](options.md).
|
||||
|
||||
Все параметры для перевода можно переопределить вручную.
|
||||
```json
|
||||
{
|
||||
"text": "hi",
|
||||
"context": "context",
|
||||
"from_lang": "en",
|
||||
"to_lang": "ru",
|
||||
"translator_plugin": "lm_studio"
|
||||
}
|
||||
```
|
||||
В этом запросе:
|
||||
* **text** - текст для перевода
|
||||
* **context** - дополнительный контекст. Например, можно указать, чтобы какое-то имя переводилось определенным образом,
|
||||
или дать понять, какого стиля переводимый текст - художественный, научный, юридический и т. д.
|
||||
* **from_lang** - двухбуквенный код языка оригинала. Коды можно посмотреть в файле [common.js](../../static/common.js) проекта.
|
||||
* **to_lang** - двухбуквенный код языка, на который нужно выполнить перевод.
|
||||
* **translator_plugin** - плагин дял перевода. Он должен быть загружен в сервис.
|
||||
|
||||
Также можно выполнить GET запрос и передать все параметры в него, например
|
||||
http://127.0.0.1:4990/translate?text=hi&from_lang=en&to_lang=ru&context=context&translator_plugin=lm_studio
|
||||
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"result": "Здравствуйте!",
|
||||
"parts": [
|
||||
{
|
||||
"text": "hi",
|
||||
"translate": "Здравствуйте!",
|
||||
"paragraph_end": true
|
||||
}
|
||||
],
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
* **result** - результат перевода.
|
||||
* **parts** - если текст в процессе перевода был разбит на несколько частей, в этом массиве будут представлены
|
||||
все части, и перевод каждой из частей. Это может пригодиться для понимания того, почему перевод был выполнен
|
||||
не полностью и подобрать более подходящие параметры разбиения текста на части.
|
||||
* * **parts[].text** - часть исходного текста, на исходном языке, после преобразований.
|
||||
* * **parts[].translate** - перевод части текста
|
||||
* * **parts[].paragraph_end** является ли часть последним предложением в параграфе (необходимо для соединения частей в поле результата `result`).
|
||||
* **error** - если перевод был выполнен успешно - null, иначе - текст ошибки.
|
||||
|
||||
### Запросы sugoi-like
|
||||
Это запросы в формате [Sugoi-Japanese-Translator](https://github.com/leminhyen2/Sugoi-Japanese-Translator),
|
||||
нужны для интеграции с другими приложениями, которые поддерживают sugoi-translator - таким образом, указав в настройках
|
||||
внешнего приложения адрес этого приложения, можно осуществить интеграцию.
|
||||
|
||||
Например, sugoi-translator поддерживают:
|
||||
* [translator-plusplus](https://dreamsavior.net/translator-plusplus/)
|
||||
* [LunaTranslator](https://docs.lunatranslator.org/en/)
|
||||
|
||||
Поддерживается GET и POST варианты запросов, оба этих запроса находятся по пути `/translate/sugoi-like`.
|
||||
24
main.py
24
main.py
|
|
@ -2,12 +2,16 @@ import sys
|
|||
from contextlib import asynccontextmanager
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi import FastAPI, Request, status
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.staticfiles import StaticFiles
|
||||
|
||||
from app import dto, log
|
||||
from app import dto, log, cuda
|
||||
from app.app_core import AppCore
|
||||
from app.cuda import cuda_info
|
||||
|
||||
APP_VERSION = "0.8.0"
|
||||
|
||||
|
||||
core: AppCore
|
||||
logger = log.logger()
|
||||
|
|
@ -18,7 +22,7 @@ async def lifespan(fast_api: FastAPI):
|
|||
logger.info("Starting llm-translate")
|
||||
|
||||
app.mount('/', StaticFiles(directory='static', html=True), name='static')
|
||||
cuda_info()
|
||||
cuda.cuda_info()
|
||||
|
||||
try:
|
||||
global core
|
||||
|
|
@ -35,6 +39,14 @@ async def lifespan(fast_api: FastAPI):
|
|||
app = FastAPI(lifespan=lifespan)
|
||||
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||
exc_str = f'{exc}'.replace('\n', ' ').replace(' ', ' ')
|
||||
if core.rest_log_params is not None and core.rest_log_params.translate_validation_errors:
|
||||
logger.error(f"Error validating request: {request}: {exc_str}")
|
||||
return JSONResponse(content={"error": exc_str}, status_code=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@app.get("/translate")
|
||||
async def translate_get(text: str, from_lang: str = "", to_lang: str = "", context: str = None,
|
||||
translator_plugin: str = "") -> dto.TranslateResp:
|
||||
|
|
@ -95,7 +107,7 @@ async def translate_post(req: dto.TranslateReq) -> dto.TranslateResp:
|
|||
async def translate_sugoi_like_post(req: dto.SugoiLikePostReq, from_lang: str = "", to_lang: str = "", context: str = None,
|
||||
translator_plugin: str = "") -> list[str]:
|
||||
"""
|
||||
Translate text. Request and response like a sugoi translator - https://github.com/leminhyen2/Sugoi-Japanese-Translator/tree/main
|
||||
Translate text. Request and response like a sugoi translator - https://github.com/leminhyen2/Sugoi-Japanese-Translator
|
||||
This allows the query to be used in integration programs such as a Translator++ - http://dreamsavior.net/docs/translator/
|
||||
|
||||
:param req:
|
||||
|
|
@ -132,7 +144,7 @@ async def translate_sugoi_like_post(req: dto.SugoiLikePostReq, from_lang: str =
|
|||
async def translate_sugoi_like_post(text: str, from_lang: str = "", to_lang: str = "", context: str = None,
|
||||
translator_plugin: str = "") -> dto.SugoiLikeGetResp:
|
||||
"""
|
||||
Translate text. Request and response like a sugoi translator - https://github.com/leminhyen2/Sugoi-Japanese-Translator/tree/main
|
||||
Translate text. Request and response like a sugoi translator - https://github.com/leminhyen2/Sugoi-Japanese-Translator
|
||||
This allows the query to be used in integration programs such as a Translator++ - http://dreamsavior.net/docs/translator/
|
||||
|
||||
:param str text: text to translate.
|
||||
|
|
|
|||
|
|
@ -72,6 +72,12 @@ manifest = {
|
|||
"paragraph_join_str": "\n",
|
||||
},
|
||||
},
|
||||
|
||||
"rest_log_params": {
|
||||
"translate_req_text": True,
|
||||
"translate_resp_text": True,
|
||||
"translate_validation_errors": True,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -91,5 +97,6 @@ def start_with_options(core: AppCore, manifest: dict):
|
|||
core.text_process_params = params.read_text_process_params(manifest)
|
||||
core.cache_params = params.read_cache_params(manifest)
|
||||
core.file_processing_params = params.read_file_processing_params(manifest)
|
||||
core.rest_log_params = params.read_rest_log_params(manifest)
|
||||
|
||||
return manifest
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ def start(core: AppCore):
|
|||
},
|
||||
|
||||
"file_processing": {
|
||||
"file_epub_translate": (init, file_processing, processed_file_name)
|
||||
"file_epub_translate": (init, file_processing, processed_file_name, after_processing)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,3 +96,7 @@ def processed_file_name(core: AppCore, file_struct: ProcessingFileStruct, req: P
|
|||
options = core.plugin_options(plugin_name)
|
||||
|
||||
return file_processor.file_name_from_template(file_struct=file_struct, req=req, options=options)
|
||||
|
||||
|
||||
def after_processing(core: AppCore) -> None:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ def start(core: AppCore):
|
|||
},
|
||||
|
||||
"file_processing": {
|
||||
"file_epub_translate": (init, file_processing, processed_file_name)
|
||||
"file_epub_translate": (init, file_processing, processed_file_name, after_processing)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,3 +72,7 @@ def processed_file_name(core: AppCore, file_struct: ProcessingFileStruct, req: P
|
|||
options = core.plugin_options(plugin_name)
|
||||
|
||||
return file_processor.file_name_from_template(file_struct=file_struct, req=req, options=options)
|
||||
|
||||
|
||||
def after_processing(core: AppCore) -> None:
|
||||
pass
|
||||
|
|
|
|||
130
plugins/plugin_file_media_whisper.py
Normal file
130
plugins/plugin_file_media_whisper.py
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import gc
|
||||
import os
|
||||
|
||||
import torch
|
||||
import whisper
|
||||
from whisper import utils
|
||||
|
||||
from app import file_processor, cuda
|
||||
from app.app_core import AppCore
|
||||
from app.dto import ProcessingFileDirReq, ProcessingFileResp, FileProcessingPluginInitInfo, ProcessingFileStruct
|
||||
|
||||
plugin_name = os.path.basename(__file__)[:-3] # calculating modname
|
||||
|
||||
model: whisper.Whisper | None = None
|
||||
|
||||
|
||||
def start(core: AppCore):
|
||||
manifest = { # plugin settings
|
||||
"name": "Subtitle extractor for media files (Whisper)", # name
|
||||
"version": "1.0", # version
|
||||
|
||||
"default_options": {
|
||||
"enabled": True,
|
||||
"model": "large-v3-turbo",
|
||||
"cuda": True,
|
||||
"cuda_device_index": 0,
|
||||
"unload_model_after_processing": True,
|
||||
"translate_after_processing": True,
|
||||
|
||||
"temperature": [0.0, 0.2, 0.4, 0.6],
|
||||
"condition_on_previous_text": False,
|
||||
"no_speech_threshold": 0.6,
|
||||
"word_timestamps": True,
|
||||
"hallucination_silence_threshold": 1,
|
||||
"carry_initial_prompt": False,
|
||||
"initial_prompt": "",
|
||||
"compression_ratio_threshold": 2.4,
|
||||
"logprob_threshold": -1.0,
|
||||
|
||||
"output_file_name_template": "%%source%%.src_sub",
|
||||
},
|
||||
|
||||
"file_processing": {
|
||||
"file_media_whisper_processing": (init, file_processing, processed_file_name, after_processing)
|
||||
},
|
||||
|
||||
"default_extension_processor": {
|
||||
"mpeg": True,
|
||||
"mpg": True,
|
||||
"mp4": True,
|
||||
"mp3": True,
|
||||
"avi": True,
|
||||
"wav": True,
|
||||
"mkv": True,
|
||||
"vob": True,
|
||||
"ac3": True,
|
||||
"mpa": True,
|
||||
"ogg": True,
|
||||
},
|
||||
}
|
||||
|
||||
return manifest
|
||||
|
||||
|
||||
def start_with_options(core: AppCore, manifest: dict):
|
||||
pass
|
||||
|
||||
|
||||
def init(core: AppCore) -> FileProcessingPluginInitInfo:
|
||||
ext = {"mpeg", "mpg", "mp3", "mp4", "avi", "wav", "mkv", "vob", "ac3", "mpa", "ogg"}
|
||||
|
||||
return FileProcessingPluginInitInfo(plugin_name=plugin_name, supported_extensions=ext)
|
||||
|
||||
|
||||
def file_processing(core: AppCore, file_struct: ProcessingFileStruct, req: ProcessingFileDirReq) -> ProcessingFileResp:
|
||||
options = core.plugin_options(plugin_name)
|
||||
|
||||
global model
|
||||
if model is None:
|
||||
model = whisper.load_model(name=options["model"], device=cuda.get_device_with_gpu_num(options))
|
||||
|
||||
temperature: list[float] = options["temperature"]
|
||||
transcribe = model.transcribe(audio=file_struct.path_file_in(), language=req.from_lang, verbose=False,
|
||||
temperature=tuple(temperature),
|
||||
condition_on_previous_text=options["condition_on_previous_text"],
|
||||
no_speech_threshold=options["no_speech_threshold"],
|
||||
word_timestamps=options["word_timestamps"],
|
||||
hallucination_silence_threshold=options["hallucination_silence_threshold"],
|
||||
carry_initial_prompt=options["carry_initial_prompt"],
|
||||
initial_prompt=options["initial_prompt"],
|
||||
compression_ratio_threshold=options["compression_ratio_threshold"],
|
||||
logprob_threshold=options["logprob_threshold"]
|
||||
)
|
||||
|
||||
if transcribe:
|
||||
out_file_name = processed_file_name(core=core, file_struct=file_struct, req=req)
|
||||
writer = utils.get_writer('srt', file_struct.path_out)
|
||||
writer(transcribe, out_file_name, {})
|
||||
|
||||
if options["translate_after_processing"]:
|
||||
return translate_after_processing(core=core, req=req, file_name_ext=out_file_name)
|
||||
else:
|
||||
return file_processor.get_processing_file_resp_ok(file_struct=file_struct, file_out=out_file_name)
|
||||
else:
|
||||
return file_processor.get_processing_file_resp_error(
|
||||
file_in=file_struct.file_name_ext, path_in=file_struct.path_in, error_msg="Can't get transcribe")
|
||||
|
||||
|
||||
def processed_file_name(core: AppCore, file_struct: ProcessingFileStruct, req: ProcessingFileDirReq) -> str:
|
||||
options = core.plugin_options(plugin_name)
|
||||
template: str = options["output_file_name_template"]
|
||||
|
||||
return file_processor.file_name_from_predefined_template(file_struct=file_struct, req=req,
|
||||
template=template, replace_ext="srt")
|
||||
|
||||
|
||||
def translate_after_processing(core: AppCore, req: ProcessingFileDirReq, file_name_ext: str) -> ProcessingFileResp:
|
||||
return core.process_file(req=req, root=req.directory_out, file_name=file_name_ext)
|
||||
|
||||
|
||||
def after_processing(core: AppCore) -> None:
|
||||
options = core.plugin_options(plugin_name)
|
||||
global model
|
||||
|
||||
if options["unload_model_after_processing"] and model is not None:
|
||||
model = None
|
||||
if options["cuda"]:
|
||||
torch.cuda.empty_cache()
|
||||
else:
|
||||
gc.collect()
|
||||
82
plugins/plugin_file_srt.py
Normal file
82
plugins/plugin_file_srt.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import os
|
||||
|
||||
import pysrt
|
||||
|
||||
from app import file_processor, params
|
||||
from app.app_core import AppCore
|
||||
from app.dto import ProcessingFileDirReq, ProcessingFileResp, FileProcessingPluginInitInfo, ProcessingFileStruct
|
||||
|
||||
plugin_name = os.path.basename(__file__)[:-3] # calculating modname
|
||||
|
||||
|
||||
def start(core: AppCore):
|
||||
manifest = { # plugin settings
|
||||
"name": "Translator for srt files", # name
|
||||
"version": "1.0", # version
|
||||
|
||||
"default_options": {
|
||||
"enabled": True,
|
||||
"text_format": {
|
||||
"original_prefix": "",
|
||||
"original_postfix": "",
|
||||
"translate_prefix": "<i>",
|
||||
"translate_postfix": "</i>",
|
||||
},
|
||||
"remove_src_filename_postfix": ".src_sub",
|
||||
"translate_delimiter": "\n",
|
||||
"output_file_name_template": {
|
||||
"preserve_original": "%%source%%.%%from_lang%%_%%to_lang%%",
|
||||
"without_original": "%%source%%.%%to_lang%%",
|
||||
},
|
||||
"default_extension_processor": {
|
||||
"srt": True
|
||||
},
|
||||
},
|
||||
|
||||
"file_processing": {
|
||||
"file_srt_translate": (init, file_processing, processed_file_name, after_processing)
|
||||
}
|
||||
}
|
||||
|
||||
return manifest
|
||||
|
||||
|
||||
def start_with_options(core: AppCore, manifest: dict):
|
||||
pass
|
||||
|
||||
|
||||
def init(core: AppCore) -> FileProcessingPluginInitInfo:
|
||||
return FileProcessingPluginInitInfo(plugin_name=plugin_name, supported_extensions={"srt"})
|
||||
|
||||
|
||||
def file_processing(core: AppCore, file_struct: ProcessingFileStruct, req: ProcessingFileDirReq) -> ProcessingFileResp:
|
||||
options = core.plugin_options(plugin_name)
|
||||
text_format = params.read_plugin_file_processing_text_format(options)
|
||||
|
||||
subs = pysrt.open(file_struct.path_file_in())
|
||||
for sub in subs:
|
||||
text = sub.text
|
||||
translate_req = req.translate_req(text, "")
|
||||
translate_text = core.translate(translate_req).result
|
||||
translate_text_format = text_format.translate_text(translate_text)
|
||||
|
||||
if req.preserve_original_text:
|
||||
original_text_format = text_format.original_text(text)
|
||||
sub.text = f'{original_text_format}{options["translate_delimiter"]}{translate_text_format}'
|
||||
|
||||
out_file_name = processed_file_name(core=core, file_struct=file_struct, req=req)
|
||||
|
||||
subs.save(file_struct.path_file_out(out_file_name))
|
||||
|
||||
return file_processor.get_processing_file_resp_ok(file_struct=file_struct, file_out=out_file_name)
|
||||
|
||||
|
||||
def processed_file_name(core: AppCore, file_struct: ProcessingFileStruct, req: ProcessingFileDirReq) -> str:
|
||||
options = core.plugin_options(plugin_name)
|
||||
src_postfix = options["remove_src_filename_postfix"]
|
||||
|
||||
return file_processor.file_name_from_template(file_struct=file_struct, req=req, options=options).replace(src_postfix + ".", ".")
|
||||
|
||||
|
||||
def after_processing(core: AppCore) -> None:
|
||||
pass
|
||||
|
|
@ -32,7 +32,7 @@ def start(core: AppCore):
|
|||
},
|
||||
|
||||
"file_processing": {
|
||||
"file_txt_translate": (init, file_processing, processed_file_name)
|
||||
"file_txt_translate": (init, file_processing, processed_file_name, after_processing)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,9 +84,10 @@ def file_processing(core: AppCore, file_struct: ProcessingFileStruct, req: Proce
|
|||
|
||||
def processed_file_name(core: AppCore, file_struct: ProcessingFileStruct, req: ProcessingFileDirReq) -> str:
|
||||
options = core.plugin_options(plugin_name)
|
||||
ext = "md" if options["markdown_output"] else None
|
||||
|
||||
file_name = file_processor.file_name_from_template(file_struct=file_struct, req=req, options=options)
|
||||
if options["markdown_output"]:
|
||||
file_name = file_name[:-3] + "md"
|
||||
return file_processor.file_name_from_template(file_struct=file_struct, req=req, options=options, replace_ext=ext)
|
||||
|
||||
return file_name
|
||||
|
||||
def after_processing(core: AppCore) -> None:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ def start(core: AppCore):
|
|||
|
||||
"default_options": {
|
||||
"custom_url": "http://localhost:1234", #
|
||||
"prompt": "You are a professional translator. Your task is to translate a text provided below from %%from_lang%% to %%to_lang%%.\n%%context_prompt%%\nINSTRUCTION:Carefully analyze the context. Pay special attention to Terminology, Style, Consistency. Provide only the translation. Do not include any additional information, explanations, notes, or comments in your response. The output should be the pure translated text only.\nTEXT TO TRANSLATE:",
|
||||
"prompt": "You are a professional translator. Your task is to translate a text (or word) provided below from %%from_lang%% to %%to_lang%%.\n%%context_prompt%%\nINSTRUCTION:Carefully analyze the context. Pay special attention to Terminology, Style, Consistency. Provide only the translation. Do not include any additional information, explanations, notes, or comments in your response. The output should be the pure translated text only.\nTEXT TO TRANSLATE:",
|
||||
"prompt_postfix": "",
|
||||
"prompt_no_think_postfix": False,
|
||||
"use_library_for_request": True,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
torch
|
||||
torchvision
|
||||
torchaudio
|
||||
openai-whisper == 20250625
|
||||
pysrt == 1.1.2
|
||||
|
||||
uvicorn == 0.34.2
|
||||
uvicorn[standard] == 0.34.2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue