lampa-plugins

# 🔌 Lampa Plugins **Плагины для [Lampa](https://lampa.mx) и свой бэкенд: топы трекеров, Plex-синк, база отметок** [![License: MIT](https://img.shields.io/badge/License-MIT-0x3654.svg)](LICENSE) [![Pages](https://img.shields.io/badge/install-GitHub%20Pages-222222.svg)](https://0x3654.github.io/lampa-plugins/) [![tracker-top](https://img.shields.io/badge/ghcr-tracker--top-2496ED.svg)](https://github.com/0x3654/lampa-plugins/pkgs/container/tracker-top) [![marks](https://img.shields.io/badge/ghcr-marks-2496ED.svg)](https://github.com/0x3654/lampa-plugins/pkgs/container/marks) [![Go](https://img.shields.io/badge/Go-1.27-00ADD8.svg)](https://go.dev) [Установка](#-установка) · [Top](#-top--адекватный-топ-вместо-ленты) · [Torrent Send](#-torrent-send--отправка-в-торрент-клиент) · [Plex Sync](#-plex-sync--статус-просмотра) · [marks](#-marks--база-отметок) · [t.js](#-tjs--бутстрап) · [tracker-top](#-tracker-top--сервер-топов) · [Планы](#-планы) · [Разработка](#-разработка) · [English](#english) Lampa Plugins comic

📦 Установка

В Lampa: Настройки → Расширения → «+», вставить URL (на каждом устройстве отдельно; PWA на iPhone — отдельно от вкладки Safari):

Плагин URL
Top https://0x3654.github.io/lampa-plugins/top.js
Torrent Send https://0x3654.github.io/lampa-plugins/transmission-send.js
Plex Sync https://0x3654.github.io/lampa-plugins/plex-sync.js
всё сразу https://0x3654.github.io/t.js (бутстрап, см. ниже)

🏆 Top — «адекватный топ» вместо ленты

Три пункта в главном меню — основной плагин.

Топ · TMDB

Популярное и лучшее по TMDB — по три смысла на фильмы и сериалы: тренды недели, лучшее по рейтингу, новинки 2025+. Переключение варианта — кнопка вправо. Карточки и пагинация нативные, запросы идут через API Lampa (учитывается прокси TMDB из настроек).

Топ · трекеры

Топ раздач NNMClub/RUTOR (только видео-категории) с постерами и метаданными TMDB. Клик — обычная страница фильма в Lampa; раздачи одного фильма схлопываются в одну позицию — качество и озвучку выбираешь дальше в парсере. Сортировка (вправо): по сидам сейчас или «классика» — завершённость за всё время (NNM o=6). Несматчившееся с TMDB (софт, игры, сборники) не показывается.

Лист фильтров (вправо) повторяет штатный фильтр торрентов: качество, озвучки, тумблеры.

Seasonvar

Зеркало seasonvar.ru + статусы просмотра аккаунта. Четыре варианта (кнопка вправо): Топ, Новинки, Обновления — блоки главной сайта — и Отслеживаемые — закладки аккаунта («на чём остановились») с плашками прогресса 2/8 и ✓ за целиком просмотренные сезоны. Карточки нативные: айтем seasonvar матчится с TMDB, клик открывает обычную страницу сериала — поиск раздач дальше штатный парсер Lampa.

Статусы пишутся в обе стороны:

Учётка — в Настройки → Seasonvar (e-mail + пароль): сервер единожды логинится и возвращает устройству куку сессии svid1 (живёт ~30 дней, обновляется кнопкой «Сессия»). Креды на сервере не хранятся; у сайта нет CORS, поэтому всё ходит через tracker-top /seasonvar/* тем же origin.

Настройки

Настройки → Топ:

Параметр Что делает
Адрес сервера топа URL tracker-top (рабочий — https://top.0x3654.com)
«Топ» вместо главной при запуске открывается последний вариант «Топа» вместо ленты
Мин. качество (трекеры) Любое / 720p+ / 1080p+ / 2160p+
Озвучка 1 / Озвучка 2 (трекеры) какие плашки озвучки показывать (напр. Дубляж + Многоголосый)
Скрывать просмотренные исключать просмотренное по истории Lampa, на сервере
Только с раздачами в «Топе · TMDB» показывать только фильмы, у которых на трекерах есть раздача под наши фильтры
Скрыть сериалы «Топ · трекеры»: только фильмы
Только на русском скрывать раздачи/карточки совсем без русских букв в названии
Скрыть аниме «Топ · трекеры»: японские аниме прочь, мультики остаются (вкл по умолчанию)
Скрывать CAM/TS камрипы и «звук с TS» вычищаются ДО дедупа: фильм только с такими раздачами исчезает целиком, с нормальной — остаётся с ней (вкл по умолчанию)

Настройки → Seasonvar: Логин/Пароль (e-mail аккаунта seasonvar.ru), Сессия · войти/обновить (статус и перелогин), Скроббл в Seasonvar (тумблер, вкл по умолчанию).

📤 Torrent Send — отправка в торрент-клиент

Два способа отправить раздачу из меню долгого нажатия (работает с любым трекером — магнет парсера уходит как есть):

Пункт Когда показывается Что делает
Скопировать магнет есть магнет магнет в буфер обмена
Открыть магнет есть магнет window.location = magnet: → системный обработчик схемы
Добавить в Plex (всегда последним) есть магнет раздача → личная лента «авто» nnm-rss → transmission-rss качает в библиотеку Plex; повторное добавление — «Уже в ленте»

Бывший плагин nnm-auto.js влит в Torrent Send и удалён; если он остался в расширениях со старых установок — удалите его.

[!NOTE] Transmission 4.1.3 убрал CORS-заголовки из RPC, поэтому браузер/PWA не могут обращаться к нему напрямую — Torrent Send идёт через системные обработчики magnet: и нативные клиенты, а не через fetch.

[!TIP] Системного UI для схем на macOS нет — обработчик переключается записью в LaunchServices (TRG для примера, bundle id com.transgui):

defaults write com.apple.LaunchServices/com.apple.launchservices.secure LSHandlers \
  -array-add '<dict><key>LSHandlerURLScheme</key><string>magnet</string>\
<key>LSHandlerRoleAll</key><string>com.transgui</string></dict>'
killall lsd

Проверить: open 'magnet:?xt=urn:btih:0000000000000000000000000000000000000000'. Bundle id любого приложения: osascript -e 'id of app "Имя"'.

«Добавить в Plex» — доступы

Настройки → NNM RSS (nnm.0x3654.com):

Параметр Что делает
Адрес сервера URL сервиса nnm-rss (предзаполнен https://nnm.0x3654.com)
Логин / Пароль аккаунт на сервисе (регистрация на его главной); пароль лежит в localStorage устройства и в настройках скрыт точками
Проверить вход кнопка: логинится текущим логином/паролем, статус прямо в строке (✓ зелёным / ✗ красным)

Авторизация: JSON-логин → токен (кэшируется на пару сервер+логин), далее Authorization: Bearer; при 401 (смена пароля ротирует токен) — перелогин и один повтор.

🎬 Plex Sync — статус просмотра

Двусторонний статус просмотра с аккаунтом Plex (Настройки → Plex). Привязка — кодом plex.tv/link (или ручным X-Plex-Token); сервер аккаунта определяется сам, ручной LAN-адрес — опционально. До привязки аккаунта ничего не отправляется.

Направление Что происходит
Lampa → Plex досмотрели (≥ 90%) — scrobble на сервере; частичный просмотр — позиция; сняли отметку — unscrobble
Plex → Lampa «Синхронизировать» переносит просмотренное и позиции в таймлайн Lampa (прогресс на карточках), «Историю просмотров» и отметки «Просмотрено» (скрытие лент читает отметки и не зависит от истории; заодно плагин поднимает сам лимит истории лампы со 100 до 5000)

Матчинг точный, по TMDB-гуидам (tmdb://id — их несут агенты Plex Movie/Series). Локальная отметка новее входящей не затирается (LWW). С включённым «Sync Watch State» у аккаунта отметка на одном сервере сама разъедется по остальным. Адрес сервера по умолчанию — тоннель на ru2 по https (https://ru2.0x3654.com/plex; чистый http с https-страницы браузер режет как mixed content); очистите поле — плагин возьмёт адрес из списка серверов аккаунта.

Импорт пишет отметки и локально (обходя «облачную» ветку бандла, где при аккаунт-синке списки не растут вовсе), и событием в облако — иначе сверка затирает их своей копией. При включённом аккаунт-синке CUB отметки из Plex долгое время жили только в таймлайне — теперь и «История», и «Просмотрено» на месте, а открытый экран топов пересобирается по завершению импорта.

🗄 marks — база отметок

Одно стабильное хранилище всех отметок: ручные статусы карточки («Смотрю / Просмотрено / Запланировано / Продолжение / Брошено»), «История», закладки. Локальный кеш расходник, облако CUB оказалось ненадёжным («Просмотрено» не хранит вообще, историю капит 500, сверка затирает локальные списки) — поэтому отметки живут в нашем маленьком сервисе, а устройства только зеркалят его.

Механика Что происходит
pull при старте + вотчдог каждые 5 минут: недостающие отметки вливаются, снятые (tombstone) убираются, съеденное сверкой восстанавливается
push каждая отметка — ручная, из Plex-импорта, от плеера — уезжает дебаунс-пачкой с карточкой и временем
sync-up при старте локальные отметки, которых нет в базе, доталкиваются сами — база наполняется с первого запуска любого устройства, серверного засева нет
LWW запись с бóльшим временем побеждает; снятие отметки — tombstone, разъезжается тоже

Хранилище — JSON-файл с атомарной заменой (SQL на личные объёмы не окупается), доступ по секретному пути, CORS для браузерного клиента. Сборка и тесты в CI → ghcr.io/0x3654/marks; публичный инстанс — https://marks.0x3654.com.

[!NOTE] Чистое устройство или снесённый кеш — всё возвращается первым стартом: boot-pull вольёт базу, sync-up дотолкает остаток.

🚀 t.js — бутстрап

Один короткий URL для чистой установки Lampa: ставит плагины выше (плюс etor — разблокировщик торрентов — и tmdb-proxy), один раз применяет кураторский блок настроек (version-gated), выбирает ссылку TorrServer пробой, мигрирует старые URL плагинов после переименования репозитория и самолечит глюк детекции платформы Apple TV. Источник правды — репо user-site; копия здесь — зеркало.

🖥 tracker-top — сервер топов

Go-сервис экранов «Топ · трекеры» и серверной сборки TMDB-топов: один статический бинарник в scratch-образе (~11 МБ). Источники: NNMClub (tracker.php?o=10, видео-поддерево категорий парсится со страницы) и rutor (топы по разделам). Философия: фильмы для просмотра, а не лента раздач — раздачи одного фильма схлопываются в одну позицию.

GET /top?src=both|nnm|rut&cat=video|all&pages=1..3&minq=720|1080|2160&audio=all|dub|no_ts
GET /feed?...   серверная сборка TMDB-топов с фильтрами и exclude просмотренного
GET /seasonvar/...  прокси seasonvar.ru: login/home/tracking/serial/search/mark
GET /healthz

Кэш в памяти (TTL 600 с), CORS *, зеркала — env NNM_BASE / RUTOR_BASE. CI собирает multi-arch образ ghcr.io/0x3654/tracker-top (deps → test → build → scratch; сломанный парсер валит сборку; non-root, healthcheck). Публичный инстанс: https://top.0x3654.com.

🗺 Планы

Сделано из крупного:

🛠 Разработка

Разработка и тестирование — локально, против локальной лампы; main — прод (Pages отдаёт плагины устройствам, CI собирает образы tracker-top и marks).

Тесты — в контейнерах (хост не пачкаем):

docker run --rm -v "$PWD":/src -w /src node:24-alpine node smoke-top.js
docker run --rm -v "$PWD":/src -w /src node:24-alpine node smoke-test.js
docker run --rm -v "$PWD"/tracker-top:/src -w /src golang:1.27-alpine go test ./...

Локальный контур

Два контейнера на маке; остальные устройства в LAN видят его по Bonjour-имени мака (scutil --get LocalHostName → <имя-мака>.local). Конфиг nginx с same-origin-прокси /topapi — в репо: dev/nginx.conf.

# tracker-top под тестом (:8355)
docker build -t tracker-top:dev tracker-top
docker run -d --name trackertop-dev -p 8355:8355 \
    -v trackertop-dev-cache:/data tracker-top:dev

# локальная лампа (:8098): официальный вебрут + конфиг nginx, один раз:
git clone --depth 1 https://github.com/yumata/lampa ~/code/lampa
rm -rf ~/code/lampa/.git   # вебрут расходный

docker run -d --name lampa-dev -p 8098:80 \
  -v "$HOME/code/lampa":/usr/share/nginx/html:ro \
  -v "$PWD/dev/nginx.conf":/etc/nginx/conf.d/default.conf:ro \
  nginx:1.27-alpine

# синхронизация правок плагинов в вебрут (после каждой правки; одиночные
# bind-mount файлов цепляют старый inode и отдавали бы устаревший код):
cp top.js transmission-send.js t.js plex-sync.js ~/code/lampa/

Опционально: вывод исходящего трафика tracker-top (Jackett-прокси, трекеры, TMDB) через свой VPS — IP мака при интенсивной отладке быстро набирает рейт-лимиты. Достаточно чистого SSH-SOCKS-туннеля — HTTP-стек Go понимает socks5-прокси из окружения. Туннель эпемерный: поднял на сессию отладки — опусти после; без него дев-контейнер теряет внешние запросы (пулы живут на дисковом кэше):

ssh -fN -D 127.0.0.1:1080 <vps>     # любой VPS с ssh-доступом
docker run -d --name trackertop-dev -p 8355:8355 -v trackertop-dev-cache:/data \
    -e HTTP_PROXY=socks5://host.docker.internal:1080 \
    -e HTTPS_PROXY=socks5://host.docker.internal:1080 \
    -e NO_PROXY=localhost,127.0.0.1 \
    tracker-top:dev

Однократно после клона вшить в ~/code/lampa/index.html дев-хук (перед скриптом с var lampa_url) — он помечает бутстрап дев-режимом и грузит локальный /t.js:

<script>
    window.TJS_DEV = true
    ;(function waitLampa(n){
        if(window.appready || window.Lampa || n > 400){
            var s = document.createElement('script')
            // бустер: даже кэшированная страница возьмёт свежий t.js
            s.src = '/t.js?v=' + Math.floor((new Date()).getTime() / 6e4)
            document.head.appendChild(s)
        } else setTimeout(function(){ waitLampa(n + 1) }, 50)
    })(0)
</script>

Дальше открываешь страницу — бутстрап делает всё сам:

Заметки:

Деплой: пуш в main → Pages публикует плагины, CI собирает образ; публичный инстанс обновляется из registry.

Точки расширения (сверены по исходникам Lampa 3.3.3): хуки Lampa.Listener('torrent'/'torrent_file', onlong) добавляют пункты в e.menu; экраны — Lampa.InteractionCategory (приём плагина collections) + Lampa.Api.sources.tmdb.get; меню Lampa.Menu.addButton, настройки Lampa.SettingsApi.

[!IMPORTANT] Неофициальный хобби-проект, никакого отношения к Lampa, NNM Club и rutor не имеет. Парсинг трекеров опирается на гостевые страницы и может ломаться при редизайнах.

Лицензия

MIT © 0x3654

English

</div>

📦 Install

In Lampa: Settings → Extensions → «+», paste a URL (per device; on iPhone the PWA is separate from the Safari tab):

Plugin URL
Top https://0x3654.github.io/lampa-plugins/top.js
Torrent Send https://0x3654.github.io/lampa-plugins/transmission-send.js
Plex Sync https://0x3654.github.io/lampa-plugins/plex-sync.js
everything at once https://0x3654.github.io/t.js (bootstrap, see below)

🏆 Top

Two menu items instead of the default feed — this is the main plugin.

Top · TMDB

TMDB popularity screens for movies and series — three flavours each: weekly trending (what’s hot now), best rated (all-time classics) and new releases of 2025+ (six variants; the day/window tops were dropped — they overlapped the weekly trending by 78–100% and the premiere windows held 8–15 films). Switch the variant with the right button on the screen. Cards and pagination are native, requests go through the Lampa API (your TMDB proxy setting applies), so screens open instantly.

Top · trackers

Release tops of NNM Club + rutor (video categories only), enriched with TMDB posters and metadata. A click opens the native film page — quality and voice are picked later in the Lampa parser, so releases of the same film collapse into a single position. Sorting (right button): by seeders now or all-time classics (NNM o=6, rutor doesn’t take part in that mode). Releases that don’t match TMDB (software, games, packs) are not shown.

The filter sheet (right button) mirrors the native torrent-filter UI: quality, up to two voices, toggles, «Show».

Seasonvar

A mirror of seasonvar.ru plus the account’s watching statuses. Four variants (right button): Top, New, Updates — the site’s main-page blocks — and Tracked — the account’s bookmarks («where you left off») with 2/8 progress plates and a ✓ for fully watched seasons. Cards are native: a seasonvar item is matched to TMDB, a click opens the regular series page — release search continues in the stock Lampa parser.

Statuses flow both ways:

Credentials live in Settings → Seasonvar (e-mail + password): the server logs in once and hands the device the svid1 session cookie (~30 days, refreshed by the «Session» button). Credentials are not stored server-side; the site has no CORS, so everything goes through tracker-top /seasonvar/*, same origin.

Settings

Settings → Top:

Setting What it does
Top server address tracker-top URL (a working one: https://top.0x3654.com)
Top as home screen open the last used Top variant on start instead of the feed
Min quality (trackers) Any / 720p+ / 1080p+ / 2160p+
Voice 1 / Voice 2 (trackers) voice plates to show (e.g. Dub + Multi)
Hide watched exclude watched by Lampa history, server-side
With releases only in Top · TMDB show only films that have a tracker release under our filters
Hide series Top · trackers: movies only
Russian titles only hide releases/cards with no cyrillic in the title
Hide anime Top · trackers: no Japanese anime, cartoons stay (on by default)
Hide CAM/TS camrips and TS sound are cut before dedup: a film with only such releases disappears entirely, one with a proper release stays (on by default)

Settings → Seasonvar: Login/Password (seasonvar.ru account e-mail), Session · sign in / refresh (status and re-login), Scrobble to Seasonvar (toggle, on by default).

📤 Torrent Send

Two ways to send a release from the long-press menu (any tracker — the parser magnet goes as-is):

Menu item When shown What it does
Copy magnet magnet present magnet to clipboard
Open magnet magnet present window.location = magnet: → the system handler (a torrent client or a helper app)
Add to Plex (always last) magnet present pushes the release into the personal «auto» feed of nnm-rss, from which transmission-rss downloads it into the Plex library; duplicate releases answer «already in the feed»

The former nnm-auto.js plugin is merged into Torrent Send and gone; remove it from the extension list if it was ever installed.

[!NOTE] Transmission 4.1.3 removed CORS headers from its RPC, so browser/PWA clients cannot talk to it directly. That is why Torrent Send routes through system magnet: handlers and native clients instead of fetch.

macOS has no UI for URL schemes; the handler is switched via LaunchServices (TRG here as an example, bundle id com.transgui):

defaults write com.apple.LaunchServices/com.apple.launchservices.secure LSHandlers \
  -array-add '<dict><key>LSHandlerURLScheme</key><string>magnet</string>\
<key>LSHandlerRoleAll</key><string>com.transgui</string></dict>'
killall lsd

Check: open 'magnet:?xt=urn:btih:0000000000000000000000000000000000000000'. Bundle id of any app: osascript -e 'id of app "Name"'.

Add to Plex — credentials

Settings → NNM RSS (nnm.0x3654.com):

Setting What it does
Server address nnm-rss URL (prefilled https://nnm.0x3654.com)
Login / Password an account on the service (register on its front page); the password stays in device localStorage and is masked with dots
Check login button: logs in with the current credentials, status right in the row (✓ green / ✗ red)

Authorization: JSON login → token (cached per server+login), then Authorization: Bearer; on 401 (a password change rotates the token) it re-logins and retries once.

🎬 Plex Sync

Two-way watch status with a Plex account (Settings → Plex). Linking is a plex.tv/link code (or a manual X-Plex-Token); the account’s server is detected automatically, a manual LAN address is optional. Nothing is sent until an account is linked.

Direction What happens
Lampa → Plex finished (≥ 90%) — scrobble on the server; partial — playback position; unmarked — unscrobble
Plex → Lampa «Sync now» pulls watched items and positions into the Lampa timeline (progress on cards), «Viewing history» and the «Viewed» marks (hiding feeds reads the marks and doesn’t depend on history; the plugin also lifts lampa’s own history cap of 100 to 5000)

Matching is exact, by TMDB guids (tmdb://id — the Plex Movie/Series agents carry them). A local mark newer than the incoming one is never overwritten (LWW). With «Sync Watch State» enabled on the account, a mark on one server propagates to the others by itself. The server address defaults to the ru2 tunnel over https (https://ru2.0x3654.com/plex; plain http would be blocked as mixed content from an https page) — clear the field to let the account’s own server list pick the address.

The import writes marks both locally (working around the bundle’s account-sync branch where the lists never grow) and as the cloud event — otherwise the reconcile wipes them with its own copy. With the CUB account sync on, imported marks used to survive only in the timeline; now «History» and «Viewed» land too, and an open Top screen rebuilds once the import finishes.

🗄 marks

The single stable store for every watch mark — manual card statuses (watching / viewed / planned / continuing / dropped), history, bookmarks. The local cache is expendable and the CUB cloud proved unreliable (it does not store «Viewed» at all, caps history at 500 and its reconcile wipes local lists), so the marks live in our own tiny service and every device just mirrors it.

Mechanics What happens
pull on boot + a 5-minute watchdog: missing marks merge in, unmarked (tombstones) drop out, whatever a reconcile ate comes back
push every mark — manual toggles, Plex imports, the player — leaves in a debounced batch with the card and a timestamp
sync-up on boot, local marks missing from the base backfill it — the base fills itself from the first start of any device, no server-side seeding
LWW the newer timestamp wins; unmarking is a tombstone and travels too

The store is a JSON file with atomic renames (SQL does not pay off at personal scale), reached over a secret URL path, CORS-open for the browser client. Build and tests in CI → ghcr.io/0x3654/marks; the public instance serves https://marks.0x3654.com. A fresh device or a wiped cache recovers everything on first start.

🚀 t.js bootstrap

One short URL for a fresh Lampa install: adds the plugins above (plus the tmdb-proxy), applies a curated settings block once (version-gated), picks the TorrServer link by probing (embedded on Apple TV, the Mac’s own TorrServer on desktops, the public ru2 server elsewhere), unlocks the torrent features natively (no etor cub plugin), migrates old plugin URLs after the repository rename and self-heals the Apple TV platform detection glitch. Source of truth lives in the user-site repo; the copy here mirrors it.

On the clone itself the settings UI carries no foreign sync: the «Синхронизация» (CUB account) section and the head profile button are removed, and the card statuses are unlocked from the CUB premium gate — every status flows into the marks base instead.

🖥 tracker-top

The Go service behind «Top · trackers» and the server-side TMDB screens: one static binary in a scratch image (~11 MB). Sources: NNM Club (tracker.php?o=10, the video category subtree parsed from the page) and rutor (per-category tops). Philosophy: films for browsing, not a release feed — releases of the same film collapse into one position.

GET /top?src=both|nnm|rut&cat=video|all&pages=1..3&minq=720|1080|2160&audio=all|dub|no_ts
GET /feed?...   server-side TMDB-top assembly with filters and watched-exclude
GET /healthz

In-memory cache (TTL 600 s), CORS *, mirror override via NNM_BASE / RUTOR_BASE. CI builds a multi-arch image: ghcr.io/0x3654/tracker-top (deps → test → build → scratch; a broken parser fails the build; non-root, healthcheck). A public instance serves https://top.0x3654.com.

🗺 Roadmap

Done highlights: the seasonvar.ru sync (a Seasonvar top with read/write watching marks and auto-scrobble, branch seasonvar), the marks single store, the Apple TV embedded TorrServer over http, the cub cuts on the clone (ads/analytics/censorship/Shots), unlocked card statuses, the Plex↔Lampa sync.

🛠 Development

Everything is developed and tested locally against a local Lampa; main is production (Pages serves the plugins, CI builds the tracker-top and marks images).

Tests run in containers (host stays clean):

docker run --rm -v "$PWD":/src -w /src node:24-alpine node smoke-top.js
docker run --rm -v "$PWD":/src -w /src node:24-alpine node smoke-test.js
docker run --rm -v "$PWD"/tracker-top:/src -w /src golang:1.27-alpine go test ./...

Local loop

Two containers on the Mac; other devices on the LAN reach it by the Mac’s Bonjour name (scutil --get LocalHostName → <mac-name>.local). The nginx config with the same-origin /topapi proxy is in the repo — dev/nginx.conf.

# tracker-top under test (:8355)
docker build -t tracker-top:dev tracker-top
docker run -d --name trackertop-dev -p 8355:8355 \
    -v trackertop-dev-cache:/data tracker-top:dev

# local Lampa (:8098): official webroot + nginx conf, once:
git clone --depth 1 https://github.com/yumata/lampa ~/code/lampa
rm -rf ~/code/lampa/.git   # disposable static webroot

docker run -d --name lampa-dev -p 8098:80 \
  -v "$HOME/code/lampa":/usr/share/nginx/html:ro \
  -v "$PWD/dev/nginx.conf":/etc/nginx/conf.d/default.conf:ro \
  nginx:1.27-alpine

# sync plugin edits into the webroot (after every edit; single-file bind
# mounts would pin the old inode and serve stale code):
cp top.js transmission-send.js t.js plex-sync.js ~/code/lampa/

Optional: route the tracker-top container’s outbound traffic (Jackett proxy, trackers, TMDB) through a VPS — the Mac’s IP collects rate limits fast during heavy debugging. A plain SSH SOCKS tunnel is enough; Go’s HTTP stack honours a socks5 proxy from the environment. The tunnel is ephemeral: raise it for a debugging session and drop it after — with it down the dev container loses external fetches (pools hold on the disk cache):

ssh -fN -D 127.0.0.1:1080 <vps>     # any VPS you can ssh to
docker run -d --name trackertop-dev -p 8355:8355 -v trackertop-dev-cache:/data \
    -e HTTP_PROXY=socks5://host.docker.internal:1080 \
    -e HTTPS_PROXY=socks5://host.docker.internal:1080 \
    -e NO_PROXY=localhost,127.0.0.1 \
    tracker-top:dev

One-time, after cloning the webroot, inject the dev hook into ~/code/lampa/index.html (before the var lampa_url script) — it flags the bootstrap for dev and loads the local /t.js:

<script>
    window.TJS_DEV = true
    ;(function waitLampa(n){
        if(window.appready || window.Lampa || n > 400){
            var s = document.createElement('script')
            // bust: even a cached page always fetches a fresh t.js
            s.src = '/t.js?v=' + Math.floor((new Date()).getTime() / 6e4)
            document.head.appendChild(s)
        } else setTimeout(function(){ waitLampa(n + 1) }, 50)
    })(0)
</script>

Open the page and the bootstrap does the rest:

Notes:

Deploy: push to main → Pages publishes the plugins, CI builds the image; the public instance updates from the registry.

Extension points (verified against the Lampa 3.3.3 sources): hooks Lampa.Listener('torrent'/'torrent_file', onlong) push menu items into e.menu; screens use Lampa.InteractionCategory (the collections-plugin reception) + Lampa.Api.sources.tmdb.get; menus Lampa.Menu.addButton, settings Lampa.SettingsApi.

[!IMPORTANT] Unofficial hobby project, no affiliation with Lampa, NNM Club or rutor. Tracker parsing follows guest-accessible pages and may break on redesigns.