@emaxe/tuigram 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +15 -0
- package/CHANGELOG.md +21 -1
- package/CHANGELOG.ru.md +21 -1
- package/README.md +47 -2
- package/README.ru.md +47 -2
- package/package.json +2 -1
- package/src/cli/videoSetup.js +187 -0
- package/src/config.js +6 -0
- package/src/index.js +8 -0
- package/src/state.js +31 -8
- package/src/telegram/dialogs.js +6 -2
- package/src/telegram/formatter.js +1 -1
- package/src/telegram/messages.js +90 -6
- package/src/ui/app.js +200 -35
- package/src/ui/components/chatList.js +156 -100
- package/src/ui/components/chatView.js +259 -78
- package/src/ui/components/inputBox.js +12 -4
- package/src/ui/components/modals/actionModal.js +5 -0
- package/src/ui/components/modals/helpModal.js +2 -1
- package/src/ui/components/modals/videoPlayerModal.js +274 -0
- package/src/ui/screen.js +108 -0
- package/src/ui/theme.js +1 -0
- package/src/utils/image.js +310 -6
- package/src/utils/mouse.js +1 -1
- package/src/utils/video.js +495 -0
package/.env.example
CHANGED
|
@@ -35,6 +35,21 @@ SHOW_IMAGES=true
|
|
|
35
35
|
IMAGE_MAX_WIDTH=36
|
|
36
36
|
IMAGE_MAX_HEIGHT=14
|
|
37
37
|
|
|
38
|
+
# ── Video Playback Preferences ────────────────────────────────────────────────
|
|
39
|
+
# Включение воспроизведения видео в терминале в псевдографике (true/false)
|
|
40
|
+
# Для работы требуется ffmpeg (автоустановка: tuigram install-video)
|
|
41
|
+
ENABLE_VIDEO=false
|
|
42
|
+
|
|
43
|
+
# Частота кадров при воспроизведении видео (FPS, 1..30, по умолчанию 15)
|
|
44
|
+
VIDEO_FPS=15
|
|
45
|
+
|
|
46
|
+
# Воспроизведение звуковой дорожки видеофайла (true/false)
|
|
47
|
+
VIDEO_AUDIO=true
|
|
48
|
+
|
|
49
|
+
# Путь к ffmpeg/ffplay (если они не в PATH и не в папке данных tuigram)
|
|
50
|
+
# FFMPEG_PATH=/usr/local/bin/ffmpeg
|
|
51
|
+
# FFPLAY_PATH=/usr/local/bin/ffplay
|
|
52
|
+
|
|
38
53
|
# ── Proxy Settings ────────────────────────────────────────────────────────────
|
|
39
54
|
# Поддерживаются HTTP и SOCKS5 (как с авторизацией, так и без).
|
|
40
55
|
# Формат единого URL:
|
package/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
13
13
|
|
|
14
14
|
- English localization of the TUI and CLI interface strings.
|
|
15
15
|
|
|
16
|
+
## [1.5.0] — 2026-09-02
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Video playback support in ANSI pseudographics (Unicode Half-Block) with synchronized audio track.
|
|
21
|
+
- New CLI command `tuigram install-video` to check and automatically install static `ffmpeg` binaries and enable video in `.env`.
|
|
22
|
+
- New configuration settings in `.env`: `ENABLE_VIDEO`, `VIDEO_FPS`, `VIDEO_AUDIO`, `FFMPEG_PATH`, `FFPLAY_PATH`.
|
|
23
|
+
- Video player modal with playback controls (`[Space]` for pause/resume, `[r]` to replay, `[Esc]` / `[q]` to exit).
|
|
24
|
+
- Interactive video playback via message action menu and by clicking on video message preview in chat view.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Enhanced unread message badge in dialog list: counters above 99 are shown as `[99+]`, and the right block (time and unread badge) is always right-aligned, truncating long chat titles with an ellipsis when space is constrained.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Opening a chat with unread messages now scrolls and positions directly at the first unread message with an unread separator divider instead of always jumping to the last message.
|
|
33
|
+
- Unread message badge and counter in the dialog list now dynamically decrease in real-time as messages are scrolled and read in the active chat.
|
|
34
|
+
|
|
16
35
|
## [1.4.0] — 2026-09-01
|
|
17
36
|
|
|
18
37
|
### Added
|
|
@@ -176,7 +195,8 @@ First public release.
|
|
|
176
195
|
- A WCAG contrast test (3:1 threshold) applied to every theme **after** conversion
|
|
177
196
|
to xterm-256 — the colors are checked exactly as the user sees them.
|
|
178
197
|
|
|
179
|
-
[Unreleased]: https://github.com/emaxe/tuigram/compare/v1.
|
|
198
|
+
[Unreleased]: https://github.com/emaxe/tuigram/compare/v1.5.0...HEAD
|
|
199
|
+
[1.5.0]: https://github.com/emaxe/tuigram/compare/v1.4.0...v1.5.0
|
|
180
200
|
[1.4.0]: https://github.com/emaxe/tuigram/compare/v1.3.0...v1.4.0
|
|
181
201
|
[1.3.0]: https://github.com/emaxe/tuigram/compare/v1.2.0...v1.3.0
|
|
182
202
|
[1.2.0]: https://github.com/emaxe/tuigram/compare/v1.1.0...v1.2.0
|
package/CHANGELOG.ru.md
CHANGED
|
@@ -13,6 +13,25 @@
|
|
|
13
13
|
|
|
14
14
|
- Английская локализация строк интерфейса TUI и CLI.
|
|
15
15
|
|
|
16
|
+
## [1.5.0] — 2026-09-02
|
|
17
|
+
|
|
18
|
+
### Добавлено
|
|
19
|
+
|
|
20
|
+
- Поддержка воспроизведения видео в терминале в псевдографике (Unicode Half-Block) с синхронной звуковой дорожкой.
|
|
21
|
+
- Консольная команда `tuigram install-video` для проверки, автоматической загрузки статической сборки `ffmpeg` и активации настройки в `.env`.
|
|
22
|
+
- Новые параметры конфигурации в `.env`: `ENABLE_VIDEO`, `VIDEO_FPS`, `VIDEO_AUDIO`, `FFMPEG_PATH`, `FFPLAY_PATH`.
|
|
23
|
+
- Модальный видеоплеер с управлением воспроизведением (`[Пробел]` — пауза/воспроизведение, `[r]` — перезапуск с начала, `[Esc]` / `[q]` — закрыть).
|
|
24
|
+
- Запуск воспроизведения через меню действий над сообщением («▶️ Воспроизвести видео») и по прямому клику мыши по превью видео в ленте.
|
|
25
|
+
|
|
26
|
+
### Изменено
|
|
27
|
+
|
|
28
|
+
- Доработан показ количества непрочитанных сообщений в списке диалогов: если непрочитанных более 99, отображается `[99+]`, а блок времени и непрочитанных всегда прижат к правому краю с сокращением названия диалога с троеточием при нехватке места.
|
|
29
|
+
|
|
30
|
+
### Исправлено
|
|
31
|
+
|
|
32
|
+
- При переходе в диалог с непрочитанными сообщениями лента теперь позиционируется на первом непрочитанном сообщении с визуальным разделителем «Непрочитанные сообщения», как в официальном клиенте Telegram (ранее всегда открывалось последнее сообщение).
|
|
33
|
+
- Бейдж и счётчик непрочитанных сообщений в списке диалогов теперь динамически уменьшаются в реальном времени по мере пролистывания и прочтения сообщений в активном чате.
|
|
34
|
+
|
|
16
35
|
## [1.4.0] — 2026-09-01
|
|
17
36
|
|
|
18
37
|
### Добавлено
|
|
@@ -182,7 +201,8 @@
|
|
|
182
201
|
- Тест контраста по WCAG (порог 3:1) для каждой темы — **после** конверсии в xterm-256,
|
|
183
202
|
то есть ровно в том виде, в каком цвет увидит пользователь.
|
|
184
203
|
|
|
185
|
-
[Unreleased]: https://github.com/emaxe/tuigram/compare/v1.
|
|
204
|
+
[Unreleased]: https://github.com/emaxe/tuigram/compare/v1.5.0...HEAD
|
|
205
|
+
[1.5.0]: https://github.com/emaxe/tuigram/compare/v1.4.0...v1.5.0
|
|
186
206
|
[1.4.0]: https://github.com/emaxe/tuigram/compare/v1.3.0...v1.4.0
|
|
187
207
|
[1.3.0]: https://github.com/emaxe/tuigram/compare/v1.2.0...v1.3.0
|
|
188
208
|
[1.2.0]: https://github.com/emaxe/tuigram/compare/v1.1.0...v1.2.0
|
package/README.md
CHANGED
|
@@ -57,6 +57,7 @@ TuiGram lets you use Telegram entirely from the terminal: browse your dialog lis
|
|
|
57
57
|
- [Sending files and images](#-sending-files-and-images)
|
|
58
58
|
- [Themes](#-themes)
|
|
59
59
|
- [Inline image previews](#️-inline-image-previews)
|
|
60
|
+
- [Video playback in pseudographics](#-video-playback-in-pseudographics)
|
|
60
61
|
- [Proxy configuration](#-proxy-configuration)
|
|
61
62
|
- [Command line usage (CLI)](#️-command-line-usage-cli)
|
|
62
63
|
- [Project structure](#-project-structure)
|
|
@@ -72,17 +73,18 @@ TuiGram lets you use Telegram entirely from the terminal: browse your dialog lis
|
|
|
72
73
|
- **A complete interactive TUI**:
|
|
73
74
|
- Two-pane adaptive layout (dialog list on the left, history and input box on the right);
|
|
74
75
|
- Keyboard and mouse control (left click selects a message, right click opens the action
|
|
75
|
-
menu, clicking an image opens it full screen, wheel scrolling);
|
|
76
|
+
menu, clicking an image opens it full screen, clicking a video plays it, wheel scrolling);
|
|
76
77
|
- Chat categories: All, Direct messages, Groups, Channels, Bots, Unread;
|
|
77
78
|
- Instant search and filtering of chats by title and `@username` (`/`);
|
|
78
79
|
- Live typing indicator ("… is typing");
|
|
79
80
|
- Colored rendering of Telegram entities (bold, italic, monospace code, URLs, mentions, spoilers);
|
|
80
81
|
- High-resolution inline image previews (Unicode Half-Block `▀` pixel art);
|
|
82
|
+
- Terminal video and video note playback (Unicode Half-Block + synchronized audio);
|
|
81
83
|
- Media attachment indicators (photo, video, document, voice, sticker, poll);
|
|
82
84
|
- Message reactions (👍, 🔥, ❤️);
|
|
83
85
|
- Infinite upward pagination of message history (`PageUp` / `Ctrl+U`);
|
|
84
86
|
- Contextual quick-reply (`Ctrl+R`) and edit (`Ctrl+E`) modes;
|
|
85
|
-
- Modal windows: Help (`F1` / `?`), Chat info (`Ctrl+P`), Action menu (`Ctrl+A`), Send file (`Ctrl+O`).
|
|
87
|
+
- Modal windows: Help (`F1` / `?`), Chat info (`Ctrl+P`), Action menu (`Ctrl+A`), Send file (`Ctrl+O`), Full-screen image viewer, Video player.
|
|
86
88
|
- **Standalone CLI mode**:
|
|
87
89
|
- Send messages and files straight from the command line;
|
|
88
90
|
- List dialogs and print chat history in the terminal;
|
|
@@ -472,6 +474,7 @@ TuiGram fully supports mouse interaction in terminals that support mouse reporti
|
|
|
472
474
|
| **Left click** | **Message in the feed** | **Select the message — a `▌` bar marks it on the left** |
|
|
473
475
|
| **Right click** | **Message in the feed** | **Open the action menu (reactions, reply, edit, download, delete)** |
|
|
474
476
|
| **Left click** | **Image preview inside a message** | **Open the image full screen** |
|
|
477
|
+
| **Left click** | **Video preview inside a message** | **Play video in modal player** |
|
|
475
478
|
| Left click | Empty area of the feed | Focus the message feed |
|
|
476
479
|
| Left click | Header logo 🚀 | Open Help window |
|
|
477
480
|
| Left click | Header active chat title | Open Chat Info window |
|
|
@@ -590,6 +593,41 @@ IMAGE_MAX_HEIGHT=14
|
|
|
590
593
|
|
|
591
594
|
---
|
|
592
595
|
|
|
596
|
+
## 🎬 Video playback in pseudographics
|
|
597
|
+
|
|
598
|
+
TuiGram supports video playback directly in the terminal using Unicode Half-Block characters (`▀`) and 24-bit RGB truecolor, with synchronized audio playback via system audio or `ffplay`.
|
|
599
|
+
|
|
600
|
+
### Setup
|
|
601
|
+
Video playback requires `ffmpeg`. You can install it and enable video playback automatically with a single command:
|
|
602
|
+
```bash
|
|
603
|
+
tuigram install-video
|
|
604
|
+
```
|
|
605
|
+
This command checks for existing `ffmpeg`, downloads a static binary if needed, and sets `ENABLE_VIDEO=true` in your `.env`.
|
|
606
|
+
|
|
607
|
+
### Configuration in `.env`:
|
|
608
|
+
```env
|
|
609
|
+
# Enable video playback in terminal (true/false)
|
|
610
|
+
ENABLE_VIDEO=true
|
|
611
|
+
|
|
612
|
+
# Playback frame rate (FPS, 1..30, default: 15)
|
|
613
|
+
VIDEO_FPS=15
|
|
614
|
+
|
|
615
|
+
# Play audio track (true/false)
|
|
616
|
+
VIDEO_AUDIO=true
|
|
617
|
+
|
|
618
|
+
# Optional custom binary paths
|
|
619
|
+
# FFMPEG_PATH=/usr/local/bin/ffmpeg
|
|
620
|
+
# FFPLAY_PATH=/usr/local/bin/ffplay
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### Controls:
|
|
624
|
+
- Click on any video message preview or choose **▶️ Play video** from the action menu (`[Enter]` / right click).
|
|
625
|
+
- `[Space]` — Pause / Resume
|
|
626
|
+
- `[r]` — Replay from beginning
|
|
627
|
+
- `[Esc]` or `[q]` — Close video player
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
593
631
|
## 🌐 Proxy configuration
|
|
594
632
|
|
|
595
633
|
TuiGram supports routing MTProto connections through HTTP (including HTTPS CONNECT) and SOCKS5/SOCKS4 proxies — both with and without username/password authentication.
|
|
@@ -628,6 +666,9 @@ clone, substitute `node bin/tuigram.js` for `tuigram`):
|
|
|
628
666
|
# Authorization
|
|
629
667
|
tuigram login
|
|
630
668
|
|
|
669
|
+
# Install video playback dependencies & enable video in .env
|
|
670
|
+
tuigram install-video
|
|
671
|
+
|
|
631
672
|
# List dialogs
|
|
632
673
|
tuigram dialogs --limit 30
|
|
633
674
|
|
|
@@ -684,13 +725,17 @@ TuiGram/
|
|
|
684
725
|
│ │ ├── chatInfoModal.js # Chat info window
|
|
685
726
|
│ │ ├── actionModal.js # Message action menu
|
|
686
727
|
│ │ ├── fileModal.js # Send-file dialog
|
|
728
|
+
│ │ ├── imageViewerModal.js # Full-screen image viewer
|
|
729
|
+
│ │ ├── videoPlayerModal.js # Video player modal
|
|
687
730
|
│ │ └── confirmModal.js # Confirmation dialog
|
|
688
731
|
│ ├── cli/
|
|
689
732
|
│ │ ├── cliCommands.js # Standalone CLI commands
|
|
690
733
|
│ │ ├── init.js # tuigram init / paths — setup and diagnostics
|
|
734
|
+
│ │ ├── videoSetup.js # tuigram install-video — ffmpeg setup & config
|
|
691
735
|
│ │ └── formatters.js # Console table and log formatters
|
|
692
736
|
│ └── utils/
|
|
693
737
|
│ ├── image.js # Image decoding, resizing and Half-Block ANSI rendering
|
|
738
|
+
│ ├── video.js # Video playback utilities, ffmpeg detection & decoding
|
|
694
739
|
│ ├── storage.js # File operations and session persistence
|
|
695
740
|
│ └── time.js # Time and date formatting
|
|
696
741
|
├── scripts/
|
package/README.ru.md
CHANGED
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
- [Отправка файлов и картинок](#-отправка-файлов-и-картинок)
|
|
54
54
|
- [Темы оформления](#-темы-оформления)
|
|
55
55
|
- [Превью изображений в сообщениях](#️-превью-изображений-в-сообщениях)
|
|
56
|
+
- [Воспроизведение видео в псевдографике](#-воспроизведение-видео-в-псевдографике)
|
|
56
57
|
- [Прокси-сервер](#-прокси-сервер)
|
|
57
58
|
- [Использование через консоль (CLI)](#️-использование-через-консоль-cli)
|
|
58
59
|
- [Структура проекта](#-структура-проекта)
|
|
@@ -68,17 +69,18 @@
|
|
|
68
69
|
- **Полноценный интерактивный TUI**:
|
|
69
70
|
- Двухпанельный адаптивный интерфейс (список чатов слева + история и поле ввода справа);
|
|
70
71
|
- Поддержка управления клавиатурой и мышью (левый клик — выделение сообщения,
|
|
71
|
-
правый — меню действий, клик по картинке — просмотр на весь экран, колесо — прокрутка);
|
|
72
|
+
правый — меню действий, клик по картинке — просмотр на весь экран, клик по видео — воспроизведение, колесо — прокрутка);
|
|
72
73
|
- Категории чатов: Все, Личные (ЛС), Группы, Каналы, Боты, Непрочитанные;
|
|
73
74
|
- Мгновенный поиск и фильтрация чатов по названию и `@username` (`/`);
|
|
74
75
|
- Отображение статуса набора текста («Собеседник печатает...») в реальном времени;
|
|
75
76
|
- Цветовое форматирование Telegram Entities (Bold, Italic, Monospace Code, URLs, Mentions, Spoilers);
|
|
76
77
|
- Цветные превью изображений высокого разрешения прямо в чате (Unicode Half-Block `▀` pixel art);
|
|
78
|
+
- Воспроизведение видео и видеозаметок в псевдографике (Unicode Half-Block + синхронный звук);
|
|
77
79
|
- Индикаторы медиа-вложений (фото, видео, документы, голосовые, стикеры, опросы);
|
|
78
80
|
- Реакции на сообщения (👍, 🔥, ❤️);
|
|
79
81
|
- Бесконечная пагинация истории сообщений вверх (`PageUp` / `Ctrl+U`);
|
|
80
82
|
- Контекстные режимы быстрого ответа (Reply `Ctrl+R`) и редактирования (Edit `Ctrl+E`);
|
|
81
|
-
- Модальные окна: Справка (`F1` / `?`), Сведения о чате (`Ctrl+P`), Меню действий (`Ctrl+A`), Отправка файла (`Ctrl+O`)
|
|
83
|
+
- Модальные окна: Справка (`F1` / `?`), Сведения о чате (`Ctrl+P`), Меню действий (`Ctrl+A`), Отправка файла (`Ctrl+O`), Просмотрщик картинок, Видеоплеер.
|
|
82
84
|
- **Автономный CLI-режим**:
|
|
83
85
|
- Быстрая отправка сообщений и файлов из командной строки;
|
|
84
86
|
- Просмотр списка диалогов и истории в терминале;
|
|
@@ -464,6 +466,7 @@ TuiGram полноценно поддерживает управление мы
|
|
|
464
466
|
| **Левый клик** | **Сообщение в ленте** | **Выделение сообщения — оно помечается полосой `▌` слева** |
|
|
465
467
|
| **Правый клик** | **Сообщение в ленте** | **Меню действий (реакции, ответ, редактирование, скачивание, удаление)** |
|
|
466
468
|
| **Левый клик** | **Превью изображения в сообщении** | **Открытие картинки на весь экран** |
|
|
469
|
+
| **Левый клик** | **Превью видео в сообщении** | **Воспроизведение видео в модальном плеере** |
|
|
467
470
|
| Левый клик | Пустое место в ленте | Переключение фокуса на ленту сообщений |
|
|
468
471
|
| Левый клик | Логотип 🚀 в шапке | Открытие окна справки |
|
|
469
472
|
| Левый клик | Название активного чата в шапке | Открытие информации о текущем чате |
|
|
@@ -585,6 +588,41 @@ IMAGE_MAX_HEIGHT=14
|
|
|
585
588
|
|
|
586
589
|
---
|
|
587
590
|
|
|
591
|
+
## 🎬 Воспроизведение видео в псевдографике
|
|
592
|
+
|
|
593
|
+
TuiGram поддерживает воспроизведение видео и видеозаметок («кружочков») прямо в терминале с помощью символов Unicode Half-Block (`▀`) и 24-битного цвета Truecolor, с синхронным воспроизведением звуковой дорожки.
|
|
594
|
+
|
|
595
|
+
### Установка зависимостей
|
|
596
|
+
Для декодирования видеопотока требуется `ffmpeg`. Установить его и активировать воспроизведение можно одной командой:
|
|
597
|
+
```bash
|
|
598
|
+
tuigram install-video
|
|
599
|
+
```
|
|
600
|
+
Команда проверит наличие `ffmpeg` в системе, при необходимости скачает статическую сборку в директорию данных TuiGram и выставит `ENABLE_VIDEO=true` в вашем `.env`.
|
|
601
|
+
|
|
602
|
+
### Настройки в `.env`:
|
|
603
|
+
```env
|
|
604
|
+
# Включение воспроизведения видео в терминале (true/false)
|
|
605
|
+
ENABLE_VIDEO=true
|
|
606
|
+
|
|
607
|
+
# Частота кадров при воспроизведении (FPS, 1..30, по умолчанию 15)
|
|
608
|
+
VIDEO_FPS=15
|
|
609
|
+
|
|
610
|
+
# Воспроизведение звуковой дорожки (true/false)
|
|
611
|
+
VIDEO_AUDIO=true
|
|
612
|
+
|
|
613
|
+
# Опциональные пути к бинарникам (если они не в PATH)
|
|
614
|
+
# FFMPEG_PATH=/usr/local/bin/ffmpeg
|
|
615
|
+
# FFPLAY_PATH=/usr/local/bin/ffplay
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Управление в плеере:
|
|
619
|
+
- Кликните по превью видео в ленте или выберите **▶️ Воспроизвести видео** в меню действий (`[Enter]` / правый клик).
|
|
620
|
+
- `[Пробел]` — пауза / возобновление
|
|
621
|
+
- `[r]` — перезапуск видео с начала
|
|
622
|
+
- `[Esc]` или `[q]` — закрыть плеер
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
588
626
|
## 🌐 Прокси-сервер
|
|
589
627
|
|
|
590
628
|
TuiGram поддерживает маршрутизацию MTProto-соединений через HTTP (включая HTTPS CONNECT) и SOCKS5/SOCKS4 прокси — как с авторизацией по логину и паролю, так и без неё.
|
|
@@ -623,6 +661,9 @@ TuiGram можно запускать в режиме консольных ут
|
|
|
623
661
|
# Авторизация
|
|
624
662
|
tuigram login
|
|
625
663
|
|
|
664
|
+
# Установка зависимостей для видео и активация в .env
|
|
665
|
+
tuigram install-video
|
|
666
|
+
|
|
626
667
|
# Список диалогов
|
|
627
668
|
tuigram dialogs --limit 30
|
|
628
669
|
|
|
@@ -679,13 +720,17 @@ TuiGram/
|
|
|
679
720
|
│ │ ├── chatInfoModal.js # Окно информации о чате
|
|
680
721
|
│ │ ├── actionModal.js # Меню действий с сообщением
|
|
681
722
|
│ │ ├── fileModal.js # Диалог отправки файла
|
|
723
|
+
│ │ ├── imageViewerModal.js # Полноэкранный просмотрщик изображений
|
|
724
|
+
│ │ ├── videoPlayerModal.js # Модальный терминальный видеоплеер
|
|
682
725
|
│ │ └── confirmModal.js # Диалог подтверждения
|
|
683
726
|
│ ├── cli/
|
|
684
727
|
│ │ ├── cliCommands.js # Автономные CLI команды
|
|
685
728
|
│ │ ├── init.js # tuigram init / paths — настройка и диагностика
|
|
729
|
+
│ │ ├── videoSetup.js # tuigram install-video — установка ffmpeg и настройка
|
|
686
730
|
│ │ └── formatters.js # Консольные форматтеры таблиц и логов
|
|
687
731
|
│ └── utils/
|
|
688
732
|
│ ├── image.js # Декодирование, масштабирование и Half-Block ANSI рендеринг изображений
|
|
733
|
+
│ ├── video.js # Утилиты видеоплеера, обнаружение ffmpeg и декодирование
|
|
689
734
|
│ ├── storage.js # Файловые операции и сохранение сессий
|
|
690
735
|
│ └── time.js # Форматирование времени и дат
|
|
691
736
|
├── scripts/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emaxe/tuigram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Полнофункциональный TUI & CLI клиент Telegram на Node.js на базе MTProto (teleproto)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"menu": "./run.sh",
|
|
58
58
|
"tui": "node src/index.js tui",
|
|
59
59
|
"init": "node src/index.js init",
|
|
60
|
+
"install-video": "node src/index.js install-video",
|
|
60
61
|
"login": "node src/index.js login",
|
|
61
62
|
"dialogs": "node src/index.js dialogs",
|
|
62
63
|
"history": "node src/index.js history",
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Консольная команда установки и настройки зависимостей для воспроизведения видео.
|
|
3
|
+
* Проверяет наличие ffmpeg, скачивает статическую сборку при необходимости
|
|
4
|
+
* и активирует функционал ENABLE_VIDEO=true в файле настроек .env.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import https from "node:https";
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { bold, cyan, green, yellow, dim, red } from "colorette";
|
|
12
|
+
import { config } from "../config.js";
|
|
13
|
+
import { upsertEnv } from "./init.js";
|
|
14
|
+
import { findFfmpegPath, findFfplayPath, extractFirstFileFromZip } from "../utils/video.js";
|
|
15
|
+
|
|
16
|
+
/** Ссылки на стабильные статические сборки ffmpeg по платформам. */
|
|
17
|
+
const FFMPEG_URLS = {
|
|
18
|
+
"darwin:x64": "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-osx-64.zip",
|
|
19
|
+
"darwin:arm64": "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-osx-64.zip",
|
|
20
|
+
"linux:x64": "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-linux-64.zip",
|
|
21
|
+
"linux:arm64": "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-linux-arm-64.zip",
|
|
22
|
+
"linux:arm": "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-linux-armel-32.zip",
|
|
23
|
+
"win32:x64": "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-win-64.zip",
|
|
24
|
+
"win32:ia32": "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-win-32.zip",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Скачивает буфер по URL с поддержкой HTTP(S) редиректов.
|
|
29
|
+
* @param {string} targetUrl
|
|
30
|
+
* @param {number} [redirects=0]
|
|
31
|
+
* @returns {Promise<Buffer>}
|
|
32
|
+
*/
|
|
33
|
+
function fetchBuffer(targetUrl, redirects = 0) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
if (redirects > 5) {
|
|
36
|
+
return reject(new Error("Слишком много редиректов при загрузке"));
|
|
37
|
+
}
|
|
38
|
+
https.get(targetUrl, (res) => {
|
|
39
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
40
|
+
return resolve(fetchBuffer(res.headers.location, redirects + 1));
|
|
41
|
+
}
|
|
42
|
+
if (res.statusCode !== 200) {
|
|
43
|
+
return reject(new Error(`Ошибка HTTP ${res.statusCode}: ${res.statusMessage}`));
|
|
44
|
+
}
|
|
45
|
+
const chunks = [];
|
|
46
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
47
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
48
|
+
res.on("error", reject);
|
|
49
|
+
}).on("error", reject);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Активирует флаг ENABLE_VIDEO=true в .env файлах.
|
|
55
|
+
* @returns {string[]} список обновлённых файлов
|
|
56
|
+
*/
|
|
57
|
+
export function enableVideoInEnv() {
|
|
58
|
+
const updated = [];
|
|
59
|
+
const targets = [config.configEnvPath];
|
|
60
|
+
|
|
61
|
+
const localEnv = path.join(config.packageRoot, ".env");
|
|
62
|
+
if (fs.existsSync(localEnv) && localEnv !== config.configEnvPath) {
|
|
63
|
+
targets.push(localEnv);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const target of targets) {
|
|
67
|
+
try {
|
|
68
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
69
|
+
let text = "";
|
|
70
|
+
try {
|
|
71
|
+
text = fs.readFileSync(target, "utf8");
|
|
72
|
+
} catch {
|
|
73
|
+
text = "# TuiGram настройки\n";
|
|
74
|
+
}
|
|
75
|
+
const newText = upsertEnv(text, { ENABLE_VIDEO: "true" });
|
|
76
|
+
fs.writeFileSync(target, newText, { encoding: "utf8", mode: 0o600 });
|
|
77
|
+
updated.push(target);
|
|
78
|
+
} catch {
|
|
79
|
+
// Игнорируем ошибку записи отдельного файла
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return updated;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Консольная команда установки зависимостей для видео (`tuigram install-video`).
|
|
88
|
+
* @param {Record<string, string|boolean>} [flags]
|
|
89
|
+
* @returns {Promise<void>}
|
|
90
|
+
*/
|
|
91
|
+
export function cmdInstallVideo(flags = {}) {
|
|
92
|
+
return (async () => {
|
|
93
|
+
console.log(bold("\n🎬 TuiGram — Установка поддержки воспроизведения видео\n"));
|
|
94
|
+
|
|
95
|
+
const existingFfmpeg = findFfmpegPath();
|
|
96
|
+
const force = Boolean(flags.force);
|
|
97
|
+
|
|
98
|
+
if (existingFfmpeg && !force) {
|
|
99
|
+
console.log(green(`✓ ffmpeg обнаружен: ${existingFfmpeg}`));
|
|
100
|
+
try {
|
|
101
|
+
const ver = spawnSync(existingFfmpeg, ["-version"], { encoding: "utf8", timeout: 2000 });
|
|
102
|
+
if (ver.stdout) {
|
|
103
|
+
const firstLine = ver.stdout.trim().split(/\r?\n/)[0];
|
|
104
|
+
console.log(dim(` Версия: ${firstLine}`));
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
// Игнорируем
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const updated = enableVideoInEnv();
|
|
111
|
+
console.log(green("\n✓ Опция ENABLE_VIDEO=true активирована в .env!"));
|
|
112
|
+
for (const file of updated) {
|
|
113
|
+
console.log(dim(` Файл: ${file}`));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
printUsageInstructions();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const platformKey = `${process.platform}:${process.arch}`;
|
|
121
|
+
const downloadUrl = FFMPEG_URLS[platformKey] || FFMPEG_URLS[`${process.platform}:x64`];
|
|
122
|
+
|
|
123
|
+
if (!downloadUrl) {
|
|
124
|
+
console.log(yellow(`Платформа ${platformKey} не поддерживает автоматическую загрузку бинарника.`));
|
|
125
|
+
printManualInstallInstructions();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log(`Платформа: ${cyan(platformKey)}`);
|
|
130
|
+
console.log(`Загрузка статической сборки ffmpeg из GitHub Releases...`);
|
|
131
|
+
console.log(dim(`URL: ${downloadUrl}\n`));
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const zipBuffer = await fetchBuffer(downloadUrl);
|
|
135
|
+
console.log(`✓ Загружено ${Math.round(zipBuffer.length / 1024 / 1024 * 10) / 10} MB. Распаковка...`);
|
|
136
|
+
|
|
137
|
+
const { data } = extractFirstFileFromZip(zipBuffer);
|
|
138
|
+
const binDir = path.join(config.dataDir, "bin");
|
|
139
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
140
|
+
|
|
141
|
+
const isWin = process.platform === "win32";
|
|
142
|
+
const binaryName = isWin ? "ffmpeg.exe" : "ffmpeg";
|
|
143
|
+
const destPath = path.join(binDir, binaryName);
|
|
144
|
+
|
|
145
|
+
fs.writeFileSync(destPath, data, { mode: 0o755 });
|
|
146
|
+
fs.chmodSync(destPath, 0o755);
|
|
147
|
+
|
|
148
|
+
// Проверка запуска
|
|
149
|
+
const testRun = spawnSync(destPath, ["-version"], { encoding: "utf8", timeout: 3000 });
|
|
150
|
+
if (testRun.status !== 0) {
|
|
151
|
+
throw new Error(`Бинарник не запустился (код ошибки: ${testRun.status})`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log(green(`✓ ffmpeg успешно установлен: ${destPath}`));
|
|
155
|
+
|
|
156
|
+
const updated = enableVideoInEnv();
|
|
157
|
+
console.log(green("✓ Опция ENABLE_VIDEO=true активирована в .env!"));
|
|
158
|
+
for (const file of updated) {
|
|
159
|
+
console.log(dim(` Файл: ${file}`));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
printUsageInstructions();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.error(red(`\nОшибка автоматической установки ffmpeg: ${err.message}`));
|
|
165
|
+
printManualInstallInstructions();
|
|
166
|
+
}
|
|
167
|
+
})();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function printUsageInstructions() {
|
|
171
|
+
console.log(bold("\n▶ Как воспроизводить видео в TuiGram:"));
|
|
172
|
+
console.log(" 1. Запустите TUI интерфейс: tuigram (или npm start)");
|
|
173
|
+
console.log(" 2. В ленте чата кликните мышью по превью видео");
|
|
174
|
+
console.log(" или выберите сообщение и нажмите Enter → «Воспроизвести видео»");
|
|
175
|
+
console.log(" 3. Управление в плеере: [Пробел] — пауза/воспроизведение, [r] — с начала, [Esc] или [q] — закрыть.\n");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function printManualInstallInstructions() {
|
|
179
|
+
console.log(bold("\n📦 Установка ffmpeg вручную через системный пакетный менеджер:"));
|
|
180
|
+
console.log(" • macOS (Homebrew): brew install ffmpeg");
|
|
181
|
+
console.log(" • Ubuntu / Debian: sudo apt update && sudo apt install -y ffmpeg");
|
|
182
|
+
console.log(" • Arch Linux: sudo pacman -S ffmpeg");
|
|
183
|
+
console.log(" • Fedora: sudo dnf install ffmpeg");
|
|
184
|
+
console.log(" • Windows (winget): winget install Gyan.FFmpeg\n");
|
|
185
|
+
console.log("После установки добавьте в ваш .env файл:");
|
|
186
|
+
console.log(cyan(" ENABLE_VIDEO=true\n"));
|
|
187
|
+
}
|
package/src/config.js
CHANGED
|
@@ -255,6 +255,12 @@ export const config = {
|
|
|
255
255
|
imageMaxWidth: parseInt(process.env.IMAGE_MAX_WIDTH || "36", 10) || 36,
|
|
256
256
|
imageMaxHeight: parseInt(process.env.IMAGE_MAX_HEIGHT || "14", 10) || 14,
|
|
257
257
|
|
|
258
|
+
enableVideo: String(process.env.ENABLE_VIDEO || process.env.ENABLE_VIDEO_PLAYBACK || "false").toLowerCase() === "true",
|
|
259
|
+
videoFps: Math.min(30, Math.max(1, parseInt(process.env.VIDEO_FPS || "15", 10) || 15)),
|
|
260
|
+
videoAudio: String(process.env.VIDEO_AUDIO || "true").toLowerCase() !== "false",
|
|
261
|
+
ffmpegPath: process.env.FFMPEG_PATH || null,
|
|
262
|
+
ffplayPath: process.env.FFPLAY_PATH || null,
|
|
263
|
+
|
|
258
264
|
proxy: parseProxyConfig(),
|
|
259
265
|
|
|
260
266
|
/**
|
package/src/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
cmdListen
|
|
15
15
|
} from "./cli/cliCommands.js";
|
|
16
16
|
import { cmdInit, cmdPaths } from "./cli/init.js";
|
|
17
|
+
import { cmdInstallVideo } from "./cli/videoSetup.js";
|
|
17
18
|
import { config } from "./config.js";
|
|
18
19
|
import { red, bold } from "colorette";
|
|
19
20
|
|
|
@@ -57,6 +58,7 @@ ${bold("Настройка:")}
|
|
|
57
58
|
tuigram init Ввести ключи Telegram API (api_id / api_hash)
|
|
58
59
|
--api-id <ID> --api-hash <HASH> без диалога
|
|
59
60
|
tuigram paths Показать пути к настройкам, сессии и загрузкам
|
|
61
|
+
tuigram install-video Установить ffmpeg и включить воспроизведение видео
|
|
60
62
|
|
|
61
63
|
${bold("Консольные команды (CLI):")}
|
|
62
64
|
tuigram login Авторизация в аккаунте (телефон, код, 2FA)
|
|
@@ -104,6 +106,12 @@ async function main() {
|
|
|
104
106
|
cmdPaths();
|
|
105
107
|
process.exit(0);
|
|
106
108
|
break;
|
|
109
|
+
case "install-video":
|
|
110
|
+
case "setup-video":
|
|
111
|
+
case "install-deps":
|
|
112
|
+
await cmdInstallVideo(flags);
|
|
113
|
+
process.exit(0);
|
|
114
|
+
break;
|
|
107
115
|
case "login":
|
|
108
116
|
await cmdLogin();
|
|
109
117
|
process.exit(0);
|
package/src/state.js
CHANGED
|
@@ -45,7 +45,7 @@ class AppState extends EventEmitter {
|
|
|
45
45
|
* @returns {Array<object>}
|
|
46
46
|
*/
|
|
47
47
|
getVisibleDialogs() {
|
|
48
|
-
const tabFiltered = filterDialogsByTab(this.dialogs, this.currentFilterTab);
|
|
48
|
+
const tabFiltered = filterDialogsByTab(this.dialogs, this.currentFilterTab, this.activeChat?.id);
|
|
49
49
|
if (!this.searchQuery) return tabFiltered;
|
|
50
50
|
return searchDialogs(tabFiltered, this.searchQuery);
|
|
51
51
|
}
|
|
@@ -97,13 +97,34 @@ class AppState extends EventEmitter {
|
|
|
97
97
|
this.replyTarget = null;
|
|
98
98
|
this.editTarget = null;
|
|
99
99
|
this.selectedMessageIndex = -1;
|
|
100
|
-
if (dialog) {
|
|
101
|
-
dialog.unreadCount = 0;
|
|
102
|
-
dialog.unreadMentionsCount = 0;
|
|
103
|
-
}
|
|
104
100
|
this.emit("active_chat_changed", dialog);
|
|
105
101
|
}
|
|
106
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Обновляет счётчик непрочитанных сообщений и максимальный прочитанный ID диалога.
|
|
105
|
+
* @param {string} chatId
|
|
106
|
+
* @param {number} unreadCount
|
|
107
|
+
* @param {number} [readInboxMaxId]
|
|
108
|
+
*/
|
|
109
|
+
updateDialogUnread(chatId, unreadCount, readInboxMaxId) {
|
|
110
|
+
const dialog = this.dialogs.find((d) => d.id === chatId);
|
|
111
|
+
if (!dialog) return;
|
|
112
|
+
|
|
113
|
+
const countChanged = dialog.unreadCount !== unreadCount;
|
|
114
|
+
const idChanged = typeof readInboxMaxId === "number" && readInboxMaxId > (dialog.readInboxMaxId || 0);
|
|
115
|
+
|
|
116
|
+
if (countChanged || idChanged) {
|
|
117
|
+
dialog.unreadCount = Math.max(0, unreadCount);
|
|
118
|
+
if (dialog.unreadCount === 0) {
|
|
119
|
+
dialog.unreadMentionsCount = 0;
|
|
120
|
+
}
|
|
121
|
+
if (idChanged) {
|
|
122
|
+
dialog.readInboxMaxId = readInboxMaxId;
|
|
123
|
+
}
|
|
124
|
+
this.emit("dialogs_updated", this.getVisibleDialogs());
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
107
128
|
/**
|
|
108
129
|
* Получает список сообщений для указанного чата.
|
|
109
130
|
* @param {string} chatId
|
|
@@ -117,9 +138,11 @@ class AppState extends EventEmitter {
|
|
|
117
138
|
* Устанавливает или дополняет список сообщений для чата.
|
|
118
139
|
* @param {string} chatId
|
|
119
140
|
* @param {Array<object>} newMessages
|
|
120
|
-
* @param {boolean} [
|
|
141
|
+
* @param {boolean|object} [options=false] Если true — добавляет старые сообщения в начало
|
|
121
142
|
*/
|
|
122
|
-
setMessages(chatId, newMessages,
|
|
143
|
+
setMessages(chatId, newMessages, options = false) {
|
|
144
|
+
const prepend = typeof options === "boolean" ? options : Boolean(options?.prepend);
|
|
145
|
+
const firstUnreadId = typeof options === "object" ? (options?.firstUnreadId || null) : null;
|
|
123
146
|
const existing = this.messagesByChat.get(chatId) || [];
|
|
124
147
|
let combined = [];
|
|
125
148
|
|
|
@@ -140,7 +163,7 @@ class AppState extends EventEmitter {
|
|
|
140
163
|
combined.sort((a, b) => (a.date || 0) - (b.date || 0) || (a.id - b.id));
|
|
141
164
|
|
|
142
165
|
this.messagesByChat.set(chatId, combined);
|
|
143
|
-
this.emit("messages_updated", { chatId, messages: combined, isPrepend: prepend, isUpdate: false });
|
|
166
|
+
this.emit("messages_updated", { chatId, messages: combined, isPrepend: prepend, isUpdate: false, firstUnreadId });
|
|
144
167
|
}
|
|
145
168
|
|
|
146
169
|
/**
|