
У каждого из нас на жестких дисках копятся терабайты контента. Фильмы, скачанные «про запас», архивы семейных видео, гигабайты музыки во FLAC, которую жалко удалять, и тысячи фотографий. Но есть проблема: потреблять этот контент локально — неудобно. Когда дома лежит большой медиа‑архив — фотографии за годы, фильмы, музыка, куча видео — начинаешь задумываться, что работать с этим напрямую через файловую систему совсем не удобно. Особенно в эпоху, когда интерфейсы крупных медиа‑сервисов настолько продуманы, что сами по себе стали стандартом UX.
Сейчас почти все решения для хранения и просмотра медиа так или иначе завязаны на облака, подписки и сторонние сервисы. Plex, Jellyfin, Emby — отличные инструменты, но либо перегружены функциональностью, либо требуют настройки и дополнительной инфраструктуры.
Я хотел:
максимально простой запуск - один
.exe, никаких зависимостей;полностью локальную работу, даже без интернета;
единый красивый интерфейс, а не разрозненные панели и страницы;
стриминг в реальном времени без скачивания и кэширования гигабайт.
В итоге пользователь скачивает архив, запускает stash-server.exe - и всё. У него в трее появляется иконка, а в браузере открывается готовый медиа-хаб.

При запуске открывается Dashboard. Сразу решаем проблему "как посмотреть кино с телефона, лёжа на диване, если файл на компе". Stash генерирует QR-код и локальную ссылку. Вы просто сканируете код телефоном - и получаете доступ ко всей медиатеке в той же Wi-Fi сети. Никаких настроек IP-адресов и портов вручную.
Каждое приложение - это маленькое самостоятельное SPA. Приложение сканирует указанную для фильмов папку, находит фильмы и сериалы. Бэкенд запоминает таймкод для каждого файла. Вы можете начать смотреть фильм на ПК, закрыть браузер, уйти на кухню, открыть Stash на планшете и продолжить с той же секунды.
Видео не скачивается целиком. Реализован чанкинг (chunking) и стриминг в реальном времени. Если формат не поддерживается браузером, используется FFmpeg, транскодируя поток на лету.
Система заметок — минималистичная, быстрая, с категоризацией и возможностью улучшать текст через LLM.
Сейчас Stash имеет модуль "LLM Чат" (интерфейс а-ля ChatGPT). Если у вас установлена Ollama, Stash автоматически подцепляется к ней через API.
Это позволяет общаться с нейросетями в приятном интерфейсе, полностью офлайн.
Отдельно хочу рассказать про опыт парного программирования с ИИ (использовал Cursor с моделями GLM-4.6 и Grok 4.1 Fast).
ИИ отлично справляется с шаблонными задачами, но когда дело доходит до архитектуры, начинается цирк. Хочу поделиться несколькими конкретными моментами из кода.
Вместо того чтобы использовать стандартный std::unique_ptr или просто std::vector для буфера, ИИ решил показать, что он знает, что такое RAII, но не знает STL.
Шедевр ручного управления памятью внутри функции чтения файла:
char* chunk = new char[chunkSize];
// ИИ создает локальную структуру ТОЛЬКО для того,
// чтобы удалить этот единственный указатель в деструкторе
struct ChunkGuard {
char* ptr;
~ChunkGuard() { if (ptr) delete[] ptr; }
} guard = {chunk};
Это выглядит так, будто код писал студент, который вчера прочитал главу про деструкторы, но пропустил главу про стандартную библиотеку. Зачем? Почему? Загадка.
Читая FileHandler.cpp, складывается ощущение, что файл писали 5 разных людей, запертых в одной комнате без интернета.
В одном методе ИИ использует std::map для сопоставления значений. В соседнем методе - городит гигантскую "лестницу" из if-else, хотя задача идентична.
// Зачем нам map? Давайте сделаем 50 проверок!
if (ext == ".html") return "text/html; charset=utf-8";
if (ext == ".js") return "application/javascript; charset=utf-8";
// ... еще 30 строк ...
if (ext == ".opus") return "audio/opus";
Никакой инкапсуляции, никаких static const map. Просто хардкод, бессмысленный и беспощадный.
ИИ не очень любит разбивать код на мелкие функции. Ему проще вывалить всё в один метод. Куча вложенных циклов, перемешанных с try-catch блоками, которые просто глотают любые ошибки:
try {
for (const auto& entry : fs::directory_iterator(wMoviesPath)) {
try {
// Еще один уровень вложенности
if (entry.is_directory()) {
try {
// И еще один цикл внутри...
} catch (...) { continue; }
}
} catch (...) { continue; }
}
} catch (...) {
// Игнорируем всё, что только можно
}
У нейросетей явно заканчивается фантазия, когда дело доходит до нейминга переменных. Вместо осмысленных имен потоков, я получил это:
std::ostringstream oss;
// ... код ...
std::ostringstream oss1;
// ... код ...
std::ostringstream oss2;
oss, oss1, oss2... Спасибо, что не a, b, c.
ИИ позволяет писать код быстро. Очень быстро. Он собирает рабочее решение из кусков, но абсолютно не заботится о красоте, памяти и поддержке.
Работать с ИИ в C++ - это как вести команду из неопытных стажёров: если их направлять, давать чёткие указания и объяснять контекст, они действительно делают много полезной работы. Они ускоряют рутинные части, экономят часы на шаблонном коде и помогают думать быстрее. Но если пустить их в свободное плавание - проект внезапно наполняется странными конструкциями, копиями функций, несистемными решениями и удивительными артефактами памяти.
Однако, проект работает, и вполне справляется со своей функцией. Дальше - оптимизация, добавление новых функций.
Исходники доступны на GitHub, там же можно скачать установщик для Windows. Больше информации о проекте в моем Telegram канале. Там я пишу обо всех обновлениях проекта. Если есть идеи для улучшения и отзывы, пишите мне в Telegram.