Publish bytecode release package

This commit is contained in:
RIMPAC 2026-06-19 16:36:24 +03:00
commit 2f72a440d1
26 changed files with 306 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
.venv/
__pycache__/
*.py[cod]
build/
tmp/
generated/
vendor/ffmpeg/*.exe
vendor/ffmpeg/*.zip
vendor/ffmpeg/_extract/
vendor/gifsicle/gifsicle.exe
vendor/gifsicle/node_modules/
vendor/gifsicle/package.json
vendor/gifsicle/package-lock.json
streamdeck/**/node_modules/
streamdeck/**/logs/
*.log

89
README.md Normal file
View File

@ -0,0 +1,89 @@
# rimpac_gif_creator
Локальная Windows-утилита для записи выделенной области экрана в маленький GIF через вложенный `ffmpeg.exe`.
## Запуск
1. Один раз установите локальный FFmpeg, если `vendor/ffmpeg/ffmpeg.exe` отсутствует:
```powershell
powershell -ExecutionPolicy Bypass -File .\tools\install_ffmpeg.ps1
```
2. Установите локальный Gifsicle для дополнительного сжатия, если `vendor/gifsicle/gifsicle.exe` отсутствует:
```powershell
powershell -ExecutionPolicy Bypass -File .\tools\install_gifsicle.ps1
```
3. Запустите приложение двойным кликом по `rimpac_gif_creator.vbs`.
`run.bat` оставлен как технический запуск с консолью, если нужно увидеть ошибки.
## Тесты
Быстрый набор unit/integrity-тестов запускается без дополнительных зависимостей:
```powershell
python -m unittest discover -s tests -v
```
Тесты проверяют логику версий и обновлений, утилиты сборки GIF, кастомный установщик, Stream Deck-плагин, тёмный логотип, а также то, что публичный release-payload не содержит исходники приложения и установщика.
## Установка и обновления
Для продуктовой сборки используется кастомный русский GUI-инсталлятор:
```powershell
powershell -ExecutionPolicy Bypass -File .\tools\build_custom_installer.ps1 -PublicRepoUrl "https://git.safe-cap.com/RIMPAC/Gif_Generator_Releases.git"
```
Скрипт готовит приватную промежуточную папку `build/package/rimpac_gif_creator`, компилирует `app.py` и `updater.py` в `app.pyc` / `updater.pyc`, собирает публичный `build/release_repo` без `.py` исходников приложения, а затем упаковывает этот payload внутрь одного `build/installer/RIMPAC_GIF_Creator_Setup.exe` через PyInstaller.
Инсталлятор на русском языке, по умолчанию ставит программу в `C:\RIMPAC\GIF Creator`, создаёт ярлык `RIMPAC GIF Creator` на рабочем столе и использует иконку из `assets/rimpac_gif_creator.ico`. Это один `RIMPAC_GIF_Creator_Setup.exe`: `ffmpeg.exe`, `ffprobe.exe`, `gifsicle.exe` и release-payload кладутся внутрь установщика заранее, без видимых командных строк, UAC-запросов и загрузок после установки. В главном окне установщика есть явная галка Stream Deck с объяснением: если у пользователя есть Stream Deck, инсталлятор дополнительно ставит плагин в `%APPDATA%\Elgato\StreamDeck\Plugins`; если галочку не ставить, устанавливается только программа и ярлык.
В папку установки также кладётся `Uninstall.exe`. Установщик регистрирует удаление в Windows для текущего пользователя, поэтому программу можно удалить через `Приложения и компоненты` или прямым запуском `C:\RIMPAC\GIF Creator\Uninstall.exe`. Удалятор убирает папку программы, ярлык с рабочего стола, запись удаления Windows и, по галочке, Stream Deck-плагин.
Текущая версия release-пакета: `beta 0.1`. Версия хранится в `version.json` и поднимается только вручную перед выпуском новой версии.
Кнопка `Проверить обновления` запускает `updater.pyc`: он делает `git fetch`, читает `version.json` из установленной программы и из `origin/main`, а затем обновляется только если в публичном релизном репозитории версия новее и канал совпадает. Если версия такая же, новый коммит в релизном git не будет устанавливаться автоматически. При наличии новой версии updater закрывает программу, выполняет `git pull --ff-only`, обновляет зависимости из `requirements.txt` и запускает программу снова. За счёт git скачиваются только изменившиеся объекты, а не весь проект архивом.
Публичный URL обновлений хранится в `update_config.json`: `https://git.safe-cap.com/RIMPAC/Gif_Generator_Releases.git`. В этом репозитории должны лежать только release-файлы (`*.pyc`, конфиги, иконки, launcher-скрипты), без `app.py` и `updater.py`.
## Управление
- Зажмите левую кнопку мыши и выделите прямоугольник.
- До начала записи можно тянуть рамку за края/углы, чтобы изменить размер, или за центр, чтобы перенести всю область.
- На панели рамки кнопка `Папка` открывает `generated`, кнопка `Очист.` удаляет все GIF из этой папки.
- Нажмите красный круг, чтобы начать запись.
- Во время записи красный круг становится квадратом, а рамка светится красным.
- Во время записи большой оверлей скрывается: остаются только тонкая красная рамка и маленькая панель, а клики за их пределами проходят в игру/систему.
- Нажмите красный квадрат, чтобы остановить запись.
- Нажмите крестик на панели, чтобы отменить и закрыть приложение.
- После записи у выбранной рамки появится предпросмотр: двумя ползунками можно обрезать начало и конец.
- При движении конечного ползунка предпросмотр сразу показывает последний кадр выбранного диапазона.
- На экране предпросмотра или прогресса `Escape` отменяет операцию и удаляет временные файлы.
- Нажмите `Создать GIF`.
- После нажатия `Создать GIF` окно предпросмотра заменяется экраном прогресса там, где была выбранная рамка.
- После сборки в этом же окне показывается итоговый размер GIF и статус копирования в буфер.
- После успешной сборки это же окно остается открытым, пока вы не закроете его крестиком или `Escape`.
- После галочки GIF уже лежит в буфере обмена как файл и сохраняется в `generated`.
- Кнопки `Папка GIF` / `Папка` открывают `generated`, кнопки `Очистить` / `Очист.` удаляют все GIF из этой папки.
GIF создается с частотой 12 кадров/с, шириной до 640 px, ограниченной палитрой и финальной оптимизацией через локальный `gifsicle`.
Бинарники `ffmpeg.exe`, `ffprobe.exe` и `gifsicle.exe` не хранятся в git, чтобы репозиторий не раздувался. Их можно восстановить скриптами из `tools`.
При запуске приложение автоматически открывает оверлей на том мониторе, где сейчас находится курсор. Захват кадров выполняется через `mss`, чтобы корректно работать с несколькими мониторами.
## Stream Deck
Плагин лежит в `streamdeck/com.rimpac.gif-creator.sdPlugin` и установлен в:
```text
C:\Users\User\AppData\Roaming\Elgato\StreamDeck\Plugins\com.rimpac.gif-creator.sdPlugin
```
В Stream Deck ищите категорию `RIMPAC` и кнопку `GIF Creator`.
Если кнопка не появилась сразу, перезапустите приложение Stream Deck.

BIN
Uninstall.exe Normal file

Binary file not shown.

BIN
app.pyc Normal file

Binary file not shown.

BIN
assets/installer_small.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
assets/installer_wizard.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
assets/rimpac_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/rimpac_logo_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
pillow>=11.0.0
mss>=10.0.0

3
rimpac_gif_creator.vbs Normal file
View File

@ -0,0 +1,3 @@
Set shell = CreateObject("WScript.Shell")
root = CreateObject("Scripting.FileSystemObject").GetParentFolderName(WScript.ScriptFullName)
shell.Run """" & root & "\run.bat" & """", 0, False

15
run.bat Normal file
View File

@ -0,0 +1,15 @@
@echo off
setlocal
cd /d "%~dp0"
if not exist ".venv\Scripts\python.exe" (
py -m venv .venv
".venv\Scripts\python.exe" -m pip install --upgrade pip
".venv\Scripts\python.exe" -m pip install -r requirements.txt
)
if exist "app.py" (
".venv\Scripts\python.exe" app.py
) else (
".venv\Scripts\python.exe" app.pyc
)

View File

@ -0,0 +1,45 @@
import streamDeck, { SingletonAction } from "@elgato/streamdeck";
import { spawn } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const pluginDir = dirname(dirname(fileURLToPath(import.meta.url)));
const launcherConfig = join(pluginDir, "launcher.json");
const fallbackLauncher = "F:\\AI\\TopSecretProjects\\ffmpeg-projects\\rimpac_gif_creator\\rimpac_gif_creator.vbs";
function getAppLauncher() {
if (!existsSync(launcherConfig)) {
return fallbackLauncher;
}
try {
const config = JSON.parse(readFileSync(launcherConfig, "utf8"));
return config.appLauncher || fallbackLauncher;
} catch {
return fallbackLauncher;
}
}
class LaunchGifCreatorAction extends SingletonAction {
manifestId = "com.rimpac.gif-creator.launch";
async onKeyDown(ev) {
const appLauncher = getAppLauncher();
if (!existsSync(appLauncher)) {
streamDeck.logger.error(`Launcher not found: ${appLauncher}`);
await ev.action.showAlert();
return;
}
const child = spawn("wscript.exe", [appLauncher], {
detached: true,
stdio: "ignore",
windowsHide: true,
});
child.unref();
await ev.action.showOk();
}
}
streamDeck.actions.registerAction(new LaunchGifCreatorAction());
streamDeck.connect();

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,39 @@
{
"$schema": "https://schemas.elgato.com/streamdeck/plugins/manifest.json",
"UUID": "com.rimpac.gif-creator",
"Name": "RIMPAC GIF Creator",
"Description": "Launches the local rimpac_gif_creator screen GIF recorder.",
"Author": "RIMPAC",
"Version": "1.0.0.0",
"SDKVersion": 2,
"Software": {
"MinimumVersion": "7.1"
},
"Nodejs": {
"Version": "24"
},
"OS": [
{
"Platform": "windows",
"MinimumVersion": "10"
}
],
"CodePath": "bin/plugin.js",
"Category": "RIMPAC ffmpeg",
"CategoryIcon": "imgs/actions/launch/icon",
"Icon": "imgs/actions/launch/icon",
"Actions": [
{
"UUID": "com.rimpac.gif-creator.launch",
"Name": "GIF Creator",
"Tooltip": "Start rimpac_gif_creator.",
"Icon": "imgs/actions/launch/icon",
"States": [
{
"Image": "imgs/actions/launch/key",
"Title": ""
}
]
}
]
}

View File

@ -0,0 +1,74 @@
{
"name": "com.rimpac.gif-creator.sdplugin",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "com.rimpac.gif-creator.sdplugin",
"version": "1.0.0",
"dependencies": {
"@elgato/streamdeck": "^2.0.4"
}
},
"node_modules/@elgato/schemas": {
"version": "0.4.15",
"resolved": "https://registry.npmjs.org/@elgato/schemas/-/schemas-0.4.15.tgz",
"integrity": "sha512-UpELAqazf52PFer+hqg/hvEhYoPHCH3R0af6P7Ih3Hk6xCCJENn7cA5TIpXAIGEWe6dIteHuo/e/iPGLUP7yXA==",
"license": "MIT"
},
"node_modules/@elgato/streamdeck": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@elgato/streamdeck/-/streamdeck-2.1.0.tgz",
"integrity": "sha512-d6wgSe93NbKAW0yT6/rabGCYUWic6nAoQoSLAXVadqTIUVmt+5Tfu7OaIGKnGER6AOn6dOpX6havRLFFFcUmOw==",
"license": "MIT",
"dependencies": {
"@elgato/schemas": "^0.4.14",
"@elgato/utils": "^0.4.4",
"ws": "^8.19.0"
},
"engines": {
"node": ">=20.5.1"
}
},
"node_modules/@elgato/utils": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/@elgato/utils/-/utils-0.4.5.tgz",
"integrity": "sha512-kBoqM8LEhhVLv7lNftYqaecNna3T/L85QQWU56yR1iFCvqlyyD22WmBljRcQkw5ZNvib55Br3cnrsxX3ajp+dQ==",
"license": "MIT",
"dependencies": {
"zod": "^3.25.24"
}
},
"node_modules/ws": {
"version": "8.21.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View File

@ -0,0 +1,9 @@
{
"name": "com.rimpac.gif-creator.sdplugin",
"version": "1.0.0",
"private": true,
"type": "module",
"dependencies": {
"@elgato/streamdeck": "^2.0.4"
}
}

5
update_config.json Normal file
View File

@ -0,0 +1,5 @@
{
"branch": "main",
"repo_url": "https://git.safe-cap.com/RIMPAC/Gif_Generator_Releases.git",
"remote": "origin"
}

BIN
updater.pyc Normal file

Binary file not shown.

BIN
vendor/ffmpeg/ffmpeg.exe vendored Normal file

Binary file not shown.

BIN
vendor/ffmpeg/ffprobe.exe vendored Normal file

Binary file not shown.

BIN
vendor/gifsicle/gifsicle.exe vendored Normal file

Binary file not shown.

4
version.json Normal file
View File

@ -0,0 +1,4 @@
{
"channel": "beta",
"version": "0.1"
}