Как я собрал нативный SwiftUI-контроллер для TorrServer на macOS

в 14:18, , рубрики: Apple Silicon, jackett, MacOS, open source, swift, swiftUI, TorrServer, медиатека

Зачем понадобилась нативная оболочка

Я пользуюсь TorrServer на Mac и в какой-то момент поймал себя на том, что для обычного просмотра постоянно переключаюсь между Терминалом, Web UI и плеером. Сам сервер меня устраивал — не хватало именно нормальной оболочки вокруг него.

Так появился TorrServe Silicon — open-source приложение на SwiftUI для Mac с Apple Silicon. Сначала я хотел сделать буквально две кнопки: запустить и остановить сервер. Но проект довольно быстро вырос в медиатеку, поиск через Jackett, работу с метаданными, запуск внешних плееров и управление из строки меню.

Это не WebView и не новая реализация TorrServer. Сам TorrServer остаётся бэкендом, а приложение общается с его API и берёт на себя повседневный macOS-сценарий.

Медиатека: сетка постеров, состояние раздачи и запуск воспроизведения

Медиатека: сетка постеров, состояние раздачи и запуск воспроизведения

С чего всё началось

Первый прототип решал простую задачу: найти исполняемый файл TorrServer, запустить процесс, показать состояние и корректно остановить его. Уже на этом этапе выяснилось, что «две кнопки» быстро обрастают деталями: нужно хранить путь к исполняемому файлу, проверять порт и API, показывать версию, следить за процессом и не оставлять пользователю непонятное состояние после ошибки.

В публичной сборке arm64-версия TorrServer добавляется внутрь приложения при упаковке. При этом путь к исполняемому файлу можно изменить вручную, а Web UI никуда не исчезает — его можно открыть из настроек, когда он действительно нужен.

Почему не обёртка над Web UI

Самым коротким путём был бы WebView. Но тогда интерфейс остался бы отдельным веб-приложением внутри окна macOS, а мне хотелось системного поведения: обычной навигации, работы с файлами, определения установленных плееров, menu bar, уведомлений и привычных настроек.

Поэтому приложение напрямую использует HTTP API TorrServer. Например, magnet-ссылка добавляется запросом с действием add, а .torrent-файл отправляется как multipart/form-data. Ответы декодируются в Swift-модели и уже из них строится интерфейс.

let payload: [String: Any] = [
    "action": "add",
    "link": magnet,
    "title": title,
    "poster": poster,
    "category": category,
    "save_to_db": true
]

У такого подхода есть приятный побочный эффект: Web UI остаётся независимым запасным интерфейсом, а нативное приложение не пытается подменять собой сервер.

Как устроен проект

Проект собирается через Swift Package Manager и условно разделён на три слоя:

  • Sources/App — жизненный цикл приложения, главное окно, сайдбар и общее состояние;

  • Sources/Core — клиент API TorrServer, форматирование и системные вспомогательные компоненты;

  • Sources/Features — Server, Library, Search, Metadata, Settings и Menu Bar.

Это не попытка построить идеальную универсальную архитектуру. Разделение появилось по мере роста проекта: серверная часть не должна знать, как выглядит карточка фильма, а библиотеке не нужно заниматься запуском процесса TorrServer.

Библиотека поверх данных TorrServer

Список материалов приходит от самого TorrServer. Приложение синхронизирует его со своим локальным кэшем метаданных и показывает в трёх вариантах: компактным списком, сеткой постеров или подробными карточками.

Magnet-ссылку можно вставить прямо в приложении, а .torrent-файл — выбрать через стандартную панель macOS. После добавления библиотека обновляется из состояния сервера, поэтому TorrServe Silicon не ведёт отдельную «истину» о торрентах.

Метаданные необязательны. Сейчас поддерживаются TMDB, OMDb, КиноПоиск и AniList. Можно задать порядок обычных провайдеров; если один источник не нашёл материал, приложение переходит к следующему. AniList проверяется отдельно для аниме. Английские описания при желании переводятся на русский средствами Apple Translation.

Источники метаданных и настройки для аниме

Источники метаданных и настройки для аниме

Поиск через Jackett — отдельная возможность

Я сознательно не делал Jackett обязательной зависимостью. Без него продолжают работать запуск сервера, библиотека, метаданные и воспроизведение. Подключение нужно только разделу поиска: пользователь указывает адрес и API-ключ своего экземпляра Jackett.

Клиент обращается к Torznab API, получает результаты, отбрасывает записи без доступной ссылки и сортирует оставшиеся сначала по числу сидов, затем по дате. Результатом может быть magnet-ссылка или скачанный .torrent-файл — оба варианта дальше передаются TorrServer тем же API-слоем.

Воспроизведение средствами macOS

TorrServer готовит поток, а воспроизведение я оставил специализированным приложениям. Сейчас можно выбрать QuickTime Player, IINA, VLC, Infuse, системное приложение по умолчанию или указать собственный плеер.

Перед запуском TorrServe Silicon проверяет, установлено ли выбранное приложение, находит его по bundle identifier и передаёт URL потока через NSWorkspace. Благодаря этому плеер остаётся обычным отдельным macOS-приложением, а не встраивается внутрь интерфейса.

Управление сервером и menu bar

В настройках сервера доступны запуск и остановка, сведения о версии и хранилище, параметры кэша, очистка кэша, обновления и диагностика исполняемого файла, порта, API, Web UI и плееров.

Настройки сервера, хранилища и кэша

Настройки сервера, хранилища и кэша

В строке меню можно видеть состояние сервера и скорость активного потока, последний материал, быстрые действия и QR-код локального Web UI. Секции можно скрывать и менять местами перетаскиванием. Для меня это оказалось удобнее отдельного окна, когда нужно только проверить состояние или остановить сервер.

Сборка и ограничения

Проект рассчитан на macOS 15 и новее и только на Apple Silicon. Интерфейс написан на SwiftUI; там, где нужны системные возможности macOS, используется AppKit. На macOS 26 приложение использует нативные материалы Liquid Glass, на более ранних поддерживаемых версиях — совместимые системные материалы.

Исходники собираются актуальным Xcode. Скрипт сборки создаёт TorrServer.app, а отдельный скрипт упаковывает DMG. Публичные локальные сборки используют ad-hoc подпись и сейчас не проходят нотаризацию Apple, поэтому при первом запуске macOS может потребовать ручное подтверждение в настройках безопасности. Я считаю важным говорить об этом прямо, а не прятать ограничение в конце страницы Releases.

git clone https://github.com/HolyMayhem/TorrServe-Silicon.git
cd TorrServe-Silicon
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift test
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer ./scripts/build-app.sh

Что получилось

В итоге проект решает ровно ту проблему, с которой начинался: для обычного использования TorrServer больше не нужно держать в голове команды и постоянно ходить в Web UI. При этом сервер остаётся самостоятельным проектом, Jackett и источники метаданных подключаются по желанию, а воспроизведение выполняют привычные плееры.

Исходники, инструкция по установке и готовые сборки находятся на GitHub: HolyMayhem/TorrServe-Silicon. Сам TorrServer — отдельный проект YouROK/TorrServer.

Буду рад обратной связи от пользователей TorrServer и разработчиков macOS-приложений. Особенно интересно, какие сценарии управления сервером или библиотекой у вас не укладываются в текущий интерфейс и где нативная оболочка пока всё ещё заставляет возвращаться в Web UI.

Автор: StrayUglyDog

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js