commit 9214407a0369fdba012c0386862854de7a9c7ca2 Author: Hermes Date: Sat Aug 8 01:40:23 2026 +0200 init: Lampa mymovies static server (nginx + CORS) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..af8bee6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +.gitignore +.dockerignore +Dockerfile +README.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ff02415 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM nginx:alpine +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY . /usr/share/nginx/html diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc6129c --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# Lampa Plugin — «Мои фильмы» + +Плагин добавляет в меню Lampa раздел **«Мои фильмы»**: каталог фильмов с обложками, +описанием и торрентами. Клик по фильму → детали → «Смотреть» → воспроизведение +через настроенный в Lampa torrserver (localhost:8090). + +## Структура на сервере + +Всё раздаётся по одному базовому URL (например `https://movies.example.com/`): + +``` +server-root/ +├── catalog.json описание каталога (массив фильмов) +├── covers/ +│ ├── inception.jpg +│ └── interstellar.jpg +├── torrents/ +│ ├── inception.torrent +│ └── interstellar.torrent +└── plugin.js (можно раздавать отсюда же или с отдельного хоста) +``` + +`catalog.json` — см. `catalog.example.json`. Поля фильма: + +| поле | обязательно | назначение | +|-----------------|-------------|---------------------------------------------| +| `id` | да | уникальный id | +| `title` | да | название | +| `original_title`| нет | оригинальное название | +| `year` | нет | год | +| `genre` | нет | массив жанров | +| `rating` | нет | рейтинг | +| `description` | нет | описание | +| `poster` | нет | путь к обложке в `covers/` (или полный URL) | +| `torrent` | да* | путь к `.torrent` в `torrents/` (или URL) | +| `magnet` | нет | magnet-ссылка (приоритетнее `torrent`) | + +\* нужен либо `torrent`, либо `magnet`. + +## Требования к серверу + +- Отдавать `catalog.json`, `covers/*`, `torrents/*` по HTTPS (или HTTP, если Lampa на + том же устройстве). +- **CORS**: заголовок `Access-Control-Allow-Origin: *` на `catalog.json` и обложки + (Lampa грузит их из WebView). Торрент torrserver забирает сам — CORS для него не нужен, + но файл должен отдаваться с корректным типом. +- Любой статический хостинг: nginx, Caddy, GitHub Pages, хостинг торрент-бокса и т.п. + +Пример nginx: + +```nginx +location / { + add_header Access-Control-Allow-Origin *; + try_files $uri $uri/ =404; +} +``` + +## Установка плагина в Lampa + +1. Положи `plugin.js` на доступный по URL хост, запомни прямую ссылку + (например `https://movies.example.com/plugin.js`). +2. В Lampa: **Настройки → Плагины → Добавить плагин** → вставить URL `plugin.js` → OK. +3. В **Настройки → Плагины → Мои фильмы** (параметр «Адрес сервера каталога») + вписать базовый URL сервера (с `/` на конце), напр. `https://movies.example.com/`. +4. В главном меню появится пункт **«Мои фильмы»**. + +## Поток воспроизведения + +При нажатии «Смотреть» плагин передаёт torrent/magnet в `Lampa.Player`. Дальше +работает уже настроенный в Lampa torrserver (локально `localhost:8090`), который +раздаёт видеопоток плееру. Плагин сам torrserver не трогает. diff --git a/catalog.example.json b/catalog.example.json new file mode 100644 index 0000000..20f254b --- /dev/null +++ b/catalog.example.json @@ -0,0 +1,28 @@ +{ + "name": "Мой видеокаталог", + "updated": "2026-08-08", + "movies": [ + { + "id": "inception", + "title": "Начало", + "original_title": "Inception", + "year": 2010, + "genre": ["фантастика", "боевик", "триллер"], + "rating": 8.8, + "description": "Дом Кобб — талантливый вор, специалист по извлечению секретов сквозь сновидения.", + "poster": "covers/inception.jpg", + "torrent": "torrents/inception.torrent" + }, + { + "id": "interstellar", + "title": "Интерстеллар", + "original_title": "Interstellar", + "year": 2014, + "genre": ["фантастика", "драма"], + "rating": 8.6, + "description": "Группа исследователей отправляется сквозь червоточину в поисках нового дома для человечества.", + "poster": "covers/interstellar.jpg", + "torrent": "torrents/interstellar.torrent" + } + ] +} diff --git a/catalog.json b/catalog.json new file mode 100644 index 0000000..20f254b --- /dev/null +++ b/catalog.json @@ -0,0 +1,28 @@ +{ + "name": "Мой видеокаталог", + "updated": "2026-08-08", + "movies": [ + { + "id": "inception", + "title": "Начало", + "original_title": "Inception", + "year": 2010, + "genre": ["фантастика", "боевик", "триллер"], + "rating": 8.8, + "description": "Дом Кобб — талантливый вор, специалист по извлечению секретов сквозь сновидения.", + "poster": "covers/inception.jpg", + "torrent": "torrents/inception.torrent" + }, + { + "id": "interstellar", + "title": "Интерстеллар", + "original_title": "Interstellar", + "year": 2014, + "genre": ["фантастика", "драма"], + "rating": 8.6, + "description": "Группа исследователей отправляется сквозь червоточину в поисках нового дома для человечества.", + "poster": "covers/interstellar.jpg", + "torrent": "torrents/interstellar.torrent" + } + ] +} diff --git a/covers/.gitkeep b/covers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..7cb6ab0 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,29 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Каталог и обложки: нужен CORS для WebView Lampa + location = /catalog.json { + add_header Access-Control-Allow-Origin * always; + default_type application/json; + try_files /catalog.json =404; + } + + location /covers/ { + add_header Access-Control-Allow-Origin * always; + try_files $uri =404; + } + + # Торренты torrserver забирает сам (CORS не нужен), но отдадим тоже + location /torrents/ { + add_header Access-Control-Allow-Origin * always; + types { application/x-bittorrent torrent; } + try_files $uri =404; + } + + location / { + try_files $uri $uri/ =404; + } +} diff --git a/plugin.js b/plugin.js new file mode 100644 index 0000000..ae9ec4f --- /dev/null +++ b/plugin.js @@ -0,0 +1,142 @@ +(function () { + 'use strict'; + + var PLUGIN = { name: 'mymovies', title: 'Мои фильмы' }; + var DEFAULT_BASE = 'https://your-server.com/'; + + function base() { + var b = Lampa.Storage.get(PLUGIN.name + '_base', DEFAULT_BASE); + return b.replace(/\/+$/, '') + '/'; + } + function resolve(p) { + if (!p) return ''; + if (/^https?:\/\//.test(p)) return p; + if (/^magnet:/.test(p)) return p; + return base() + p.replace(/^\/+/, ''); + } + + // ---- Компонент: список фильмов ---- + Lampa.Component.add(PLUGIN.name, { + create: function () { + var comp = this; + comp.activity.loader(true); + var content = comp.activity.render().find('.main__content'); + content.html('
'); + + fetch(base() + 'catalog.json') + .then(function (r) { return r.json(); }) + .then(function (data) { + comp.activity.loader(false); + renderMovies(content.find('.movies-grid'), data.movies || []); + }) + .catch(function () { + comp.activity.loader(false); + Lampa.Noty.show('Не удалось загрузить каталог'); + }); + } + }); + + function renderMovies(grid, movies) { + grid.empty(); + if (!movies.length) { + grid.append('
Каталог пуст
'); + return; + } + movies.forEach(function (m) { + var card = $('
'); + card.append($('').attr('src', resolve(m.poster || m.cover)).attr('loading', 'lazy')); + card.append($('
').text(m.title)); + if (m.year) card.append($('
').text(m.year)); + card.on('hover:enter', function () { openMovie(m); }); + grid.append(card); + }); + Lampa.Selector.update(); + } + + // ---- Компонент: детали фильма ---- + var selected = null; + Lampa.Component.add(PLUGIN.name + '_detail', { + create: function () { + var m = selected; + if (!m) { Lampa.Activity.back(); return; } + var html = '
'; + html += '
'; + html += ''; + html += '

' + esc(m.title) + '

'; + if (m.original_title) html += '
' + esc(m.original_title) + '
'; + if (m.year) html += '
' + m.year + '
'; + if (m.genre && m.genre.length) html += '
' + m.genre.join(', ') + '
'; + if (m.rating) html += '
★ ' + m.rating + '
'; + if (m.description) html += '
' + esc(m.description) + '
'; + html += '
'; + html += '
Смотреть
'; + html += '
'; + + var content = this.activity.render().find('.main__content'); + content.html(html); + content.find('[data-act="play"]').on('hover:enter', function () { play(m); }); + Lampa.Selector.update(); + } + }); + + function openMovie(m) { + selected = m; + Lampa.Activity.push({ + url: '', + component: PLUGIN.name + '_detail', + title: m.title, + page: m.title + }); + } + + function play(m) { + var url = m.magnet ? m.magnet : resolve(m.torrent); + Lampa.Player.play({ + url: url, + title: m.title, + img: resolve(m.poster || m.cover), + season: 0, + episode: 0 + }); + } + + function esc(s) { + return String(s == null ? '' : s) + .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); + } + + // ---- Пункт меню ---- + function addMenuItem() { + if ($('.menu__item[data-component="' + PLUGIN.name + '"]').length) return; + var item = $('