(function () { 'use strict'; var PLUGIN = { name: 'mymovies', title: 'My Movies', menu_action: 'mymovies' }; var DEFAULT_BASE = 'https://movies-lmp-18.dynv6.net/'; 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(/^\/+/, ''); } function esc(s) { return String(s == null ? '' : s) .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } // Load catalog via Lampa native network layer (TV-safe, fetch fails on some devices) function loadCatalog(cb) { var url = base() + 'movies/catalog.json'; if (Lampa.Reguest && Lampa.Reguest.get) { Lampa.Reguest.get(url, function (html) { try { cb(null, JSON.parse(html)); } catch (e) { cb(e, null); } }, function (err) { cb(err || new Error('request failed'), null); }); } else { fetch(url).then(function (r) { return r.json(); }) .then(function (d) { cb(null, d); }) .catch(function (e) { cb(e, null); }); } } // Plugin templates/styles (pattern from Shikimori plugin): // own namespaced classes so they do NOT collide with Lampa native .card__img // (native card__img is hidden until its own lazy-load runs, so our must use a custom class). function injectStyles() { if (window.__mymovies_css_injected) return; window.__mymovies_css_injected = true; // Card template with poster image Lampa.Template.add('mymovies-card', '
' + '
' + '' + '
' + '
{title}
' + '
{year}
' + '
'); // Styles Lampa.Template.add('mymovies-style', ''); } // ---------- Component: movie list ---------- // Framework contract (ActivitySlide.create): // component.create(body) -> store the activity body // body.append(component.render(true)) -> render() returns a DOM node function MyMovies() { var comp = this; var body; this.create = function (b) { body = b; }; this.start = function () { if (Lampa.Selector) Lampa.Selector.update(); }; this.render = function () { injectStyles(); var html = document.createElement('div'); html.className = 'mymovies'; var grid = document.createElement('div'); grid.className = 'movies-grid'; html.appendChild(grid); if (comp.activity) comp.activity.loader(true); loadCatalog(function (err, data) { if (comp.activity) comp.activity.loader(false); if (err) { console.error('[mymovies] catalog error', err); grid.innerHTML = '
Catalog load error
'; if (Lampa.Selector) Lampa.Selector.update(); return; } var movies = (data && data.movies) || []; console.log('[mymovies] loaded', movies.length, 'films'); renderMovies($(grid), movies); }); return html; }; } Lampa.Component.add(PLUGIN.name, MyMovies); function renderMovies(grid, movies) { grid.empty(); if (!movies.length) { grid.append('
Catalog is empty
'); if (Lampa.Selector) Lampa.Selector.update(); return; } movies.forEach(function (m) { // Use plugin card template (own classes, does not collide with native card__img) var card = Lampa.Template.get('mymovies-card', { img: resolve(m.poster || m.cover), title: m.title || m.id, year: m.year || '' }); card = $(card); card.attr('data-id', esc(m.id)); card.on('hover:enter', function () { openMovie(m); }); grid.append(card); }); if (Lampa.Selector) Lampa.Selector.update(); } // ---------- Component: movie detail ---------- var selected = null; function MyMoviesDetail() { var comp = this; var body; this.create = function (b) { body = b; }; this.start = function () { if (Lampa.Selector) Lampa.Selector.update(); }; this.render = function () { var m = (comp.activity && comp.activity.object) || selected; if (!m) { Lampa.Activity.back(); return document.createElement('div'); } injectStyles(); var html = document.createElement('div'); html.className = 'mymovies-detail'; var head = document.createElement('div'); head.className = 'detail-head'; var poster = document.createElement('img'); poster.className = 'detail-poster'; poster.src = resolve(m.poster || m.cover); head.appendChild(poster); var info = document.createElement('div'); info.className = 'detail-info'; var h1 = document.createElement('h1'); h1.textContent = m.title; info.appendChild(h1); if (m.original_title) { var d = document.createElement('div'); d.className = 'detail-orig'; d.textContent = m.original_title; info.appendChild(d); } if (m.year) { var d = document.createElement('div'); d.className = 'detail-year'; d.textContent = m.year; info.appendChild(d); } if (m.genre && m.genre.length) { var d = document.createElement('div'); d.className = 'detail-genre'; d.textContent = m.genre.join(', '); info.appendChild(d); } if (m.rating) { var d = document.createElement('div'); d.className = 'detail-rating'; d.textContent = '★ ' + m.rating; info.appendChild(d); } if (m.description) { var d = document.createElement('div'); d.className = 'detail-desc'; d.textContent = m.description; info.appendChild(d); } head.appendChild(info); html.appendChild(head); var actions = document.createElement('div'); actions.className = 'detail-actions'; var btn = document.createElement('div'); btn.className = 'btn selector'; btn.setAttribute('data-act', 'play'); btn.textContent = 'Watch'; actions.appendChild(btn); html.appendChild(actions); $(btn).on('hover:enter', function () { play(m); }); return html; }; } Lampa.Component.add(PLUGIN.name + '_detail', MyMoviesDetail); function openMovie(m) { selected = m; Lampa.Activity.push({ url: '', component: PLUGIN.name + '_detail', title: m.title, page: m.title, object: m }); } 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 }); } // ---------- Menu item ---------- // Lampa builds the main menu on the 'menu:start' event, e.body is the jQuery menu root. // Remove ALL old copies from the DOM first, then add exactly one button. function makeButton() { var button = $( '' ); button.on('hover:enter', function () { Lampa.Activity.push({ url: '', component: PLUGIN.name, title: PLUGIN.title, page: PLUGIN.title }); }); return button; } var menuSubscribed = false; function addMenuButton() { // remove ALL copies everywhere in DOM, then add exactly one $('[data-action="' + PLUGIN.menu_action + '"]').remove(); $('.menu .menu__list').eq(0).append(makeButton()); if (Lampa.Selector) Lampa.Selector.update(); } function start() { if (menuSubscribed) return; menuSubscribed = true; Lampa.Listener.follow('menu', function (e) { if (e.type === 'start' && e.body) { var list = e.body.find('.menu__list'); if (!list.length) return; // guard against duplicate copies in this menu body if (list.find('[data-action="' + PLUGIN.menu_action + '"]').length) return; addMenuButton(); } }); // fallback: re-check shortly after load (covers late menu builds) setTimeout(function () { if ($('.menu .menu__list').length && !$('[data-action="' + PLUGIN.menu_action + '"]').length) { addMenuButton(); } }, 3000); } if (window.Lampa && Lampa.Listener && Lampa.Activity) { start(); } else { document.addEventListener('lampa', start); } })();