From 98e40310373c32c0f4744edddf42b0057fd1d998 Mon Sep 17 00:00:00 2001 From: DUB-AI Date: Tue, 27 Oct 2026 16:47:45 +0700 Subject: [PATCH] =?UTF-8?q?PowerShell-=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=20predict.ps1=20+=20charset=3Dutf-8=20=D0=B2=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B2=D0=B5=D1=82=D0=B0=D1=85=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=20(=D1=84=D0=B8=D0=BA=D1=81=20=D0=BA=D0=B8=D1=80?= =?UTF-8?q?=D0=B8=D0=BB=D0=BB=D0=B8=D1=86=D1=8B=20=D0=B2=20PS=205.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/integration.md | 21 +++++++++++++ examples/README.md | 1 + examples/powershell/predict.ps1 | 56 +++++++++++++++++++++++++++++++++ src/service.py | 14 ++++++++- 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 examples/powershell/predict.ps1 diff --git a/docs/integration.md b/docs/integration.md index baf7b05..5ad4f08 100644 --- a/docs/integration.md +++ b/docs/integration.md @@ -84,6 +84,27 @@ const { tracks } = await res.json(); Типизированная версия — `examples/typescript/predict.ts` (запуск: `npx tsx predict.ts --text "..."`). +### PowerShell (examples/powershell/predict.ps1) + +Скрипт (работает и в Windows PowerShell 5.1, и в PowerShell 7): + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -Text "Перевод: Дублированный | MovieDalen" +pwsh -File examples\powershell\predict.ps1 -HtmlFile page.html -Url http://host:8000 +``` + +Однострочник (POST с кириллицей; UTF-8 байтами — важно для PS 5.1, где +строковый `-Body` уходит в ISO-8859-1): + +```powershell +$b = [Text.Encoding]::UTF8.GetBytes((@{ text = "Перевод: Дублированный | MovieDalen" } | ConvertTo-Json)) +(Invoke-RestMethod -Uri http://127.0.0.1:8000/predict -Method Post -ContentType "application/json" -Body $b).tracks +``` + +Проверка живости: `Invoke-RestMethod http://127.0.0.1:8000/health`. +Ответ сервис отдаёт с `charset=utf-8`, поэтому кириллица в ответе +корректно декодируется и в PS 5.1. + ## Путь Б: ONNX без Python Экспорт (из корня проекта, после обучения): diff --git a/examples/README.md b/examples/README.md index 4f84f5f..b1fd300 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,6 +7,7 @@ | `typescript/predict.mjs` | Node.js >= 18 | HTTP | CLI-клиент сервиса: `--text` / `--html`, без зависимостей | | `typescript/predict.ts` | TypeScript | HTTP | то же с типами (`npx tsx predict.ts --text "..."`) | | `typescript/onnx_local.mjs` | Node.js | ONNX | локальный инференс без Python (`onnxruntime-node` + `@huggingface/transformers`) | +| `powershell/predict.ps1` | PowerShell 5.1/7 | HTTP | `-Text "..."` / `-HtmlFile страница.html` (кириллица и кодировки учтены) | | `rust/http_client` | Rust | HTTP | `cargo run -- --text "..."` / `--html файл` (reqwest + tokio) | | `rust/onnx_local` | Rust | ONNX | локальный инференс (`ort` + `tokenizers`) | diff --git a/examples/powershell/predict.ps1 b/examples/powershell/predict.ps1 new file mode 100644 index 0000000..c37986f --- /dev/null +++ b/examples/powershell/predict.ps1 @@ -0,0 +1,56 @@ +# Вызов сервиса DUB-AI из PowerShell. +# +# Примеры (из корня проекта): +# powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -Text "Перевод: Дублированный | MovieDalen" +# powershell -NoProfile -ExecutionPolicy Bypass -File examples\powershell\predict.ps1 -HtmlFile page.html +# pwsh -File examples\powershell\predict.ps1 -Text "..." -Url http://host:8000 +# +# Тело запроса отправляется UTF-8 байтами — это одинаково работает и в +# Windows PowerShell 5.1, и в PowerShell 7 (в 5.1 строковый -Body без этого +# уходит в ISO-8859-1 и кириллица ломается). + +param( + [string]$Url = "http://127.0.0.1:8000", + [Parameter(Mandatory = $true, ParameterSetName = 'Text')] + [string]$Text, + [Parameter(Mandatory = $true, ParameterSetName = 'Html')] + [string]$HtmlFile +) + +$ErrorActionPreference = 'Stop' +try { [Console]::OutputEncoding = [Text.Encoding]::UTF8 } catch { } + +if ($HtmlFile) { + # [IO.File]::ReadAllText даёт чистую строку (Get-Content прикрепляет + # свойства PSPath, и ConvertTo-Json отправил бы объект вместо строки) + $path = (Resolve-Path -LiteralPath $HtmlFile).Path + $html = [IO.File]::ReadAllText($path, [Text.Encoding]::UTF8) + if ($html.Contains([string][char]0xFFFD)) { # utf-8 не вышел — пробуем cp1251 + $html = [IO.File]::ReadAllText($path, [Text.Encoding]::GetEncoding(1251)) + } + $payload = @{ html = $html } +} else { + $payload = @{ text = $Text } +} + +$json = $payload | ConvertTo-Json +$bytes = [Text.Encoding]::UTF8.GetBytes($json) + +try { + $r = Invoke-RestMethod -Uri "$Url/predict" -Method Post ` + -ContentType 'application/json' -Body $bytes +} catch { + Write-Error "Сервис недоступен ($Url). Запуск: python -m src.service`n$_" + exit 1 +} + +if ($r.tracks) { + $r.tracks | Format-Table translation_type, studio, is_18plus, + type_confidence -AutoSize + foreach ($t in $r.tracks) { + " текст: $($t.segment)" + if ($t.studio_span) { " спан: $($t.studio_span)" } + } +} else { + "дорожки озвучки не найдены" +} diff --git a/src/service.py b/src/service.py index 226a7e2..3ca133a 100644 --- a/src/service.py +++ b/src/service.py @@ -14,11 +14,22 @@ from __future__ import annotations import argparse from fastapi import FastAPI, HTTPException +from fastapi.responses import JSONResponse from pydantic import BaseModel from .predict import DubPredictor +class Utf8JSONResponse(JSONResponse): + """application/json с явным charset=utf-8. + + Без этого Windows PowerShell 5.1 (Invoke-RestMethod) декодирует ответ + как ISO-8859-1 и кириллица превращается в «кракозябры». + """ + + media_type = "application/json; charset=utf-8" + + class PredictRequest(BaseModel): """Либо text (одна дорожка), либо html (страница целиком).""" text: str | None = None @@ -27,7 +38,8 @@ class PredictRequest(BaseModel): def create_app(predictor: DubPredictor) -> FastAPI: app = FastAPI(title="DUB-AI", version="0.1.0", - description="Тип озвучки + автор озвучки") + description="Тип озвучки + автор озвучки", + default_response_class=Utf8JSONResponse) @app.get("/health") def health():