263 lines
10 KiB
JavaScript
263 lines
10 KiB
JavaScript
(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, '>').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); });
|
|
}
|
|
}
|
|
|
|
// ---------- Component: movie list ----------
|
|
function MyMovies() {
|
|
var comp = this;
|
|
|
|
this.create = function () {
|
|
return comp.render();
|
|
};
|
|
|
|
// Framework (ActivitySlide.start) calls component.start() after the
|
|
// activity is ready. Refresh the selector so focus lands on a card.
|
|
this.start = function () {
|
|
if (Lampa.Selector) Lampa.Selector.update();
|
|
};
|
|
|
|
this.render = function () {
|
|
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);
|
|
|
|
injectStyles();
|
|
|
|
loadCatalog(function (err, data) {
|
|
if (comp.activity) comp.activity.loader(false);
|
|
if (err) {
|
|
console.error('[mymovies] catalog error', err);
|
|
grid.innerHTML = '<div class="mymovies-empty">Catalog load error</div>';
|
|
Lampa.Layer.update(html);
|
|
Lampa.Layer.visible(html);
|
|
return;
|
|
}
|
|
var movies = (data && data.movies) || [];
|
|
console.log('[mymovies] loaded', movies.length, 'films');
|
|
renderMovies($(grid), movies);
|
|
Lampa.Layer.update(html);
|
|
Lampa.Layer.visible(html);
|
|
});
|
|
|
|
return html;
|
|
};
|
|
}
|
|
Lampa.Component.add(PLUGIN.name, MyMovies);
|
|
|
|
function renderMovies(grid, movies) {
|
|
grid.empty();
|
|
if (!movies.length) {
|
|
grid.append('<div class="mymovies-empty">Catalog is empty</div>');
|
|
if (Lampa.Selector) Lampa.Selector.update();
|
|
return;
|
|
}
|
|
movies.forEach(function (m) {
|
|
var card = $('<div class="card selector movie-card" data-id="' + esc(m.id) + '">');
|
|
card.append($('<img class="card__poster">').attr('src', resolve(m.poster || m.cover)).attr('loading', 'lazy'));
|
|
card.append($('<div class="card__title">').text(m.title || m.id));
|
|
if (m.year) card.append($('<div class="card__subtitle">').text(m.year));
|
|
card.on('hover:enter', function () { openMovie(m); });
|
|
grid.append(card);
|
|
});
|
|
if (Lampa.Selector) Lampa.Selector.update();
|
|
}
|
|
|
|
// Inject plugin styles so the grid looks like native Lampa movie cards
|
|
function injectStyles() {
|
|
if (window.__mymovies_css_injected) return;
|
|
window.__mymovies_css_injected = true;
|
|
var css = '' +
|
|
'.mymovies{padding:2.2em 3.4em;overflow-y:auto;height:100%;box-sizing:border-box}' +
|
|
'.movies-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:1.4em}' +
|
|
'.movie-card{background:#1c1f26;border-radius:10px;overflow:hidden;cursor:pointer;transition:transform .15s}' +
|
|
'.movie-card .card__poster{width:100%;aspect-ratio:2/3;object-fit:cover;display:block;background:#2a2e38}' +
|
|
'.movie-card .card__title{padding:.6em .7em .2em;font-size:1em;line-height:1.2;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}' +
|
|
'.movie-card .card__subtitle{padding:0 .7em .6em;font-size:.85em;opacity:.6}' +
|
|
'.movie-card.focus,.movie-card.hover{transform:scale(1.05)}' +
|
|
'.mymovies-detail{padding:3em;max-width:900px}' +
|
|
'.detail-head{display:flex;gap:2em}' +
|
|
'.detail-poster{width:220px;border-radius:10px}' +
|
|
'.detail-info h1{margin:0 0 .4em;font-size:1.8em}' +
|
|
'.detail-info>div{margin:.3em 0;opacity:.85}' +
|
|
'.detail-actions{margin-top:1.5em}' +
|
|
'.mymovies-empty{padding:2em;opacity:.6;font-size:1.2em}';
|
|
$('<style>').text(css).appendTo('body');
|
|
}
|
|
|
|
// ---------- Component: movie detail ----------
|
|
var selected = null;
|
|
function MyMoviesDetail() {
|
|
var comp = this;
|
|
|
|
this.create = function () {
|
|
return comp.render();
|
|
};
|
|
|
|
// Framework (ActivitySlide.start) calls component.start().
|
|
this.start = function () {
|
|
if (Lampa.Selector) Lampa.Selector.update();
|
|
};
|
|
|
|
this.render = function () {
|
|
var m = selected;
|
|
if (!m) { Lampa.Activity.back(); return document.createElement('div'); }
|
|
|
|
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); });
|
|
Lampa.Layer.update(html);
|
|
Lampa.Layer.visible(html);
|
|
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
|
|
});
|
|
}
|
|
|
|
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 = $(
|
|
'<li class="menu__item selector" data-action="' + PLUGIN.menu_action + '">' +
|
|
'<div class="menu__ico">' +
|
|
'<svg class="sprite-folder"><use xlink:href="#sprite-folder"></use></svg>' +
|
|
'</div>' +
|
|
'<div class="menu__text">' + PLUGIN.title + '</div>' +
|
|
'</li>'
|
|
);
|
|
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);
|
|
}
|
|
})();
|