Add base version of run-transcribe.py
This commit is contained in:
parent
8f8e07ceae
commit
624923ef5f
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -7,3 +7,5 @@ __pycache__
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
*.db-journal
|
*.db-journal
|
||||||
|
|
||||||
|
build/
|
||||||
17
.vscode/launch.json
vendored
17
.vscode/launch.json
vendored
|
|
@ -13,8 +13,19 @@
|
||||||
"FLASK_ENV": "development",
|
"FLASK_ENV": "development",
|
||||||
"FLASK_DEBUG": "1"
|
"FLASK_DEBUG": "1"
|
||||||
},
|
},
|
||||||
"justMyCode": true
|
"justMyCode": true,
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Python: VK Transcribe",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"python": "${workspaceFolder}/env/Scripts/python.exe",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"program": "${workspaceFolder}/run-transcribe.py",
|
||||||
|
"justMyCode": true,
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"args": []
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"console": "integratedTerminal"
|
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ CORS(
|
||||||
# ----------------------------------------------- WebSocket Initialization ---------------------------------------------------------------
|
# ----------------------------------------------- WebSocket Initialization ---------------------------------------------------------------
|
||||||
|
|
||||||
socketio = SocketIO(
|
socketio = SocketIO(
|
||||||
api, cors_allowed_origins="*") # В production настроить CORS строже
|
api, cors_allowed_origins="*") # В production настроить CORS строже # , async_mode='eventlet' для компиляции в exe
|
||||||
|
|
||||||
graph_history_manager.set_socketio(socketio)
|
graph_history_manager.set_socketio(socketio)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
# отключаем 40-ка строчное ограничение для этого файла
|
# отключаем 40-ка строчное ограничение для этого файла
|
||||||
# ----------------------------------------------- Imports ------------------------------------------------------------
|
# ----------------------------------------------- Imports ------------------------------------------------------------
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ MODELS: Dict[str, Dict[str, Any]] = {
|
||||||
"stream": True,
|
"stream": True,
|
||||||
"capabilities": ["vision"],
|
"capabilities": ["vision"],
|
||||||
},
|
},
|
||||||
|
"gemini-2.5-flash-openrouter": {
|
||||||
|
"name": "gemini-2.5-flash-openrouter",
|
||||||
|
"provider": "openai", # Изменено на "openai"
|
||||||
|
"model_name": "gemini-2.5-flash", # Добавлено имя модели для LangChain
|
||||||
|
"apiBase":
|
||||||
|
"https://openrouter.ai", # "generativelanguage.googleapis.com", # "https://render-service-gsu7.onrender.com/g/v1beta",
|
||||||
|
"apiKey": "AIzaSyDpueKFWVqknVKlQn6TdasLmJ2lvAUiBik",
|
||||||
|
"stream": True,
|
||||||
|
"capabilities": ["vision"],
|
||||||
|
},
|
||||||
"gemini-2.5-flash-bh": {
|
"gemini-2.5-flash-bh": {
|
||||||
"name": "gemini-2.5-flash",
|
"name": "gemini-2.5-flash",
|
||||||
"provider": "openai", # Изменено на "openai"
|
"provider": "openai", # Изменено на "openai"
|
||||||
|
|
@ -64,6 +74,26 @@ MODELS: Dict[str, Dict[str, Any]] = {
|
||||||
"stream": True,
|
"stream": True,
|
||||||
"capabilities": ["vision"],
|
"capabilities": ["vision"],
|
||||||
},
|
},
|
||||||
|
"gemini-3-flash-preview-bh": {
|
||||||
|
"name": "gemini-3-flash-preview",
|
||||||
|
"provider": "openai", # Изменено на "openai"
|
||||||
|
"model_name": "gemini-3-flash-preview", # Добавлено имя модели для LangChain
|
||||||
|
"apiBase": "https://bothub.chat/api/v2/openai/v1",
|
||||||
|
"apiKey":
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImQ3MTMwYTk0LTJiNmMtNDgzZi04Y2NiLWM5MjgwNzlmNDFlZSIsImlzRGV2ZWxvcGVyIjp0cnVlLCJpYXQiOjE3NTQ0NzYwMDcsImV4cCI6MjA3MDA1MjAwN30._64_DXNZatdd4-4VbXp8dOXdDhIlNWMQt9A_Nn6mu3g",
|
||||||
|
"stream": True,
|
||||||
|
"capabilities": ["vision"],
|
||||||
|
},
|
||||||
|
"gemini-3.1-pro-preview-bh": {
|
||||||
|
"name": "gemini-3.1-pro-preview",
|
||||||
|
"provider": "openai", # Изменено на "openai"
|
||||||
|
"model_name": "gemini-3.1-pro-preview", # Добавлено имя модели для LangChain
|
||||||
|
"apiBase": "https://bothub.chat/api/v2/openai/v1",
|
||||||
|
"apiKey":
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImQ3MTMwYTk0LTJiNmMtNDgzZi04Y2NiLWM5MjgwNzlmNDFlZSIsImlzRGV2ZWxvcGVyIjp0cnVlLCJpYXQiOjE3NTQ0NzYwMDcsImV4cCI6MjA3MDA1MjAwN30._64_DXNZatdd4-4VbXp8dOXdDhIlNWMQt9A_Nn6mu3g",
|
||||||
|
"stream": True,
|
||||||
|
"capabilities": ["vision"],
|
||||||
|
},
|
||||||
"claude-4.5-sonnet-bh": {
|
"claude-4.5-sonnet-bh": {
|
||||||
"name": "claude-4.5-sonnet-bh",
|
"name": "claude-4.5-sonnet-bh",
|
||||||
"provider": "openai", # Изменено на "openai"
|
"provider": "openai", # Изменено на "openai"
|
||||||
|
|
|
||||||
BIN
geckodriver.exe
Normal file
BIN
geckodriver.exe
Normal file
Binary file not shown.
65
obfuscate.txt
Normal file
65
obfuscate.txt
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
Для создания исполняемого файла (.exe), который запускается без установки, с обфусцированным кодом Python лучше всего использовать связку PyArmor + PyInstaller:
|
||||||
|
|
||||||
|
1. Обфускация с PyArmor:
|
||||||
|
- PyArmor шифрует и обфусцирует исходный Python-код, усложняя его анализ и обратную разработку.
|
||||||
|
- Используется командой:
|
||||||
|
```
|
||||||
|
pyarmor gen -O dist --recursive путь/к/вашему/проекту
|
||||||
|
```
|
||||||
|
или
|
||||||
|
```
|
||||||
|
pyarmor gen -O dist --recursive .
|
||||||
|
```
|
||||||
|
- Это создаст обфусцированные версии скриптов с сохранением структуры.
|
||||||
|
|
||||||
|
2. Упаковка в exe с PyInstaller:
|
||||||
|
- После обфускации используйте PyInstaller, чтобы упаковать проект в единый exe-файл с параметром `--onefile`:
|
||||||
|
```
|
||||||
|
pyinstaller --onefile --noconsole ваш_главный_скрипт.py
|
||||||
|
```
|
||||||
|
|
||||||
|
? python -m PyInstaller --onefile --noconsole run.py
|
||||||
|
? pyinstaller --onefile --noconsole --paths=./app run.py
|
||||||
|
? pyinstaller --onefile --hidden-import=eventlet.green --hidden-import=eventlet.green.thread --hidden-import=engineio.async_eventlet --hidden-import=engineio.async_drivers.eventlet --noconsole --paths=./app run.py
|
||||||
|
|
||||||
|
|
||||||
|
- Если нужно, добавляйте параметры `--add-data` для ресурсов.
|
||||||
|
|
||||||
|
3. Итог:
|
||||||
|
- Итоговый .exe-файл будет содержать интерпретатор Python, все зависимости и обфусцированный код.
|
||||||
|
- Запуск exe не требует предварительной установки Python или зависимостей.
|
||||||
|
- Такой файл удобно выкладывать в релизы GitHub и распространять пользователям.
|
||||||
|
|
||||||
|
4. Важные советы:
|
||||||
|
- Всегда тестируйте exe на чистой системе.
|
||||||
|
- PyArmor даёт хорошую защиту, но не абсолютную.
|
||||||
|
- Для повышения безопасности используйте активацию лицензий PyArmor (опционно).
|
||||||
|
- Настройте spec-файл PyInstaller, чтобы включить все необходимые модули и ресурсы.
|
||||||
|
|
||||||
|
Пример типичного workflow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Обфускация
|
||||||
|
pyarmor obfuscate --recursive your_project/
|
||||||
|
|
||||||
|
# Переход в обфусцированную папку
|
||||||
|
cd dist/your_project
|
||||||
|
|
||||||
|
# Создание exe
|
||||||
|
pyinstaller --onefile --noconsole main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Тогда в папке `dist` появится самодостаточный исполняемый файл.
|
||||||
|
|
||||||
|
Таким образом, PyArmor + PyInstaller позволяет создать exe с обфусцированным кодом, который не требует установки Python и готов к распространению через GitHub релизы.[1][7][9]
|
||||||
|
|
||||||
|
[1](https://pressanybutton.ru/post/poleznye-instrumenty/python-proektav-exe-fajl-i-obfuskaciya-koda/)
|
||||||
|
[2](https://www.youtube.com/watch?v=15ho_Zg_Kq4)
|
||||||
|
[3](https://www.youtube.com/watch?v=HtiYdCk0_J8)
|
||||||
|
[4](https://www.reddit.com/r/learnpython/comments/144tdhy/obfuscating_python_script/)
|
||||||
|
[5](https://stackoverflow.com/questions/58334936/pack-a-pyarmor-obfuscated-script-to-exe)
|
||||||
|
[6](https://qna.habr.com/q/1055278)
|
||||||
|
[7](https://pyarmor.readthedocs.io/en/v5.7.0/pack-obfuscated-scripts.html)
|
||||||
|
[8](https://github.com/dashingsoft/pyarmor)
|
||||||
|
[9](https://www.cyberforum.ru/python-ide/thread3151287.html)
|
||||||
|
[10](https://www.reddit.com/r/learnpython/comments/1bdsnce/which_one_is_best_for_code_obfuscation/)
|
||||||
|
|
@ -11,3 +11,5 @@ flask_cors==6.0.1
|
||||||
flask_socketio
|
flask_socketio
|
||||||
|
|
||||||
mistralai==1.9.10
|
mistralai==1.9.10
|
||||||
|
|
||||||
|
selenium
|
||||||
113
run-transcribe.py
Normal file
113
run-transcribe.py
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from selenium import webdriver
|
||||||
|
from selenium.webdriver.firefox.service import Service
|
||||||
|
from selenium.webdriver.firefox.options import Options
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
|
||||||
|
# --- НАСТРОЙКИ ---
|
||||||
|
OBSIDIAN_PATH = r"D:\Work\Software_Development\Projects\ttrpg-obsidian-vault\scripts\transcrib\VK_Call_Notes.md"
|
||||||
|
CHECK_INTERVAL = 0.5 # Частота проверки текста
|
||||||
|
RECONNECT_INTERVAL = 6.0 # Частота поиска вкладки
|
||||||
|
|
||||||
|
def append_to_obsidian(text, new_line=True):
|
||||||
|
"""
|
||||||
|
Записывает текст.
|
||||||
|
new_line=True: создает новую строку с таймстампом.
|
||||||
|
new_line=False: дописывает в конец существующей строки.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(OBSIDIAN_PATH, "a", encoding="utf-8") as f:
|
||||||
|
if new_line:
|
||||||
|
timestamp = datetime.now().strftime("\n[%H:%M:%S] ")
|
||||||
|
f.write(f"{timestamp}{text}")
|
||||||
|
else:
|
||||||
|
f.write(f"{text}") # f.write(f" {text}")
|
||||||
|
print(f"Дописано: {text}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка записи: {e}")
|
||||||
|
|
||||||
|
def find_vk_tab(driver):
|
||||||
|
"""Пытается найти и переключиться на вкладку ВК."""
|
||||||
|
try:
|
||||||
|
for handle in driver.window_handles:
|
||||||
|
driver.switch_to.window(handle)
|
||||||
|
if "vk.com" in driver.current_url or "Call" in driver.title:
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run_capture():
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
driver_path = os.path.join(current_dir, "geckodriver.exe")
|
||||||
|
|
||||||
|
options = Options()
|
||||||
|
options.add_argument("--debugger-address 127.0.0.1:9222")
|
||||||
|
service = Service(executable_path=driver_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
driver = webdriver.Firefox(service=service, options=options)
|
||||||
|
print("--- Соединение с браузером установлено ---")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Критическая ошибка: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
last_text = ""
|
||||||
|
active_tab_found = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# 1. Если вкладка не найдена — ищем её
|
||||||
|
if not active_tab_found:
|
||||||
|
if find_vk_tab(driver):
|
||||||
|
print(f"Нашел звонок: {driver.title}. Начинаю слежку...")
|
||||||
|
active_tab_found = True
|
||||||
|
else:
|
||||||
|
print("Жду открытия вкладки со звонком ВК...")
|
||||||
|
time.sleep(RECONNECT_INTERVAL)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 2. Попытка прочитать текст
|
||||||
|
try:
|
||||||
|
element = driver.find_element(By.CLASS_NAME, "CallsSubtitles__content")
|
||||||
|
current_full_text = element.text.strip()
|
||||||
|
|
||||||
|
if current_full_text:
|
||||||
|
# Случай 1: Это продолжение текущей фразы
|
||||||
|
if last_full_text and current_full_text.startswith(last_full_text):
|
||||||
|
# Берем только то, чего еще не было
|
||||||
|
added_part = current_full_text[len(last_full_text):] #.strip()
|
||||||
|
if added_part:
|
||||||
|
append_to_obsidian(added_part, new_line=False)
|
||||||
|
last_full_text = current_full_text
|
||||||
|
|
||||||
|
# Случай 2: Это абсолютно новая фраза (предыдущая исчезла)
|
||||||
|
elif current_full_text != last_full_text:
|
||||||
|
append_to_obsidian(current_full_text, new_line=True)
|
||||||
|
last_full_text = current_full_text
|
||||||
|
|
||||||
|
else:
|
||||||
|
# В элементе пусто — сбрасываем кэш, чтобы следующая фраза шла с новой строки
|
||||||
|
last_full_text = ""
|
||||||
|
|
||||||
|
time.sleep(CHECK_INTERVAL)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
# Если элемент потерян, сбрасываем состояние
|
||||||
|
last_full_text = ""
|
||||||
|
try:
|
||||||
|
_ = driver.current_url
|
||||||
|
except:
|
||||||
|
print("Вкладка потеряна. Ищу...")
|
||||||
|
active_tab_found = False
|
||||||
|
time.sleep(CHECK_INTERVAL)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nСкрипт остановлен.")
|
||||||
|
finally:
|
||||||
|
print("Сессия завершена.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_capture()
|
||||||
1
run.py
1
run.py
|
|
@ -10,6 +10,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# ----------------------------------------------- Main ------------------------------------------------------------
|
# ----------------------------------------------- Main ------------------------------------------------------------
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# Добавляем папку app в Python path
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'app'))
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'app'))
|
||||||
from app.api import api
|
from app.api import api
|
||||||
from app.heartbeat_monitor import HeartbeatMonitor
|
from app.heartbeat_monitor import HeartbeatMonitor
|
||||||
|
|
|
||||||
38
run.spec
Normal file
38
run.spec
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['run.py'],
|
||||||
|
pathex=['./app'],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=['eventlet.green', 'eventlet.green.thread', 'engineio.async_eventlet', 'engineio.async_drivers.eventlet'],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
noarchive=False,
|
||||||
|
optimize=0,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='run',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=False,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue
Block a user