259 lines
7.9 KiB
JavaScript
259 lines
7.9 KiB
JavaScript
(function () {
|
||
'use strict';
|
||
|
||
var FLAG = 'lampa_p365x_plugin_started_v2';
|
||
if (window[FLAG]) return;
|
||
window[FLAG] = true;
|
||
|
||
var CFG = {
|
||
baseUrl: 'https://m.porno365x.space',
|
||
// Если прямой доступ блокируется CORS, укажи прокси URL:
|
||
// например: 'https://your-domain.tld/lampa/p365x?url='
|
||
proxyPrefix: '',
|
||
requestTimeoutMs: 15000
|
||
};
|
||
|
||
var STATE = {
|
||
items: [],
|
||
page: 1,
|
||
loading: false,
|
||
mounted: false
|
||
};
|
||
|
||
function qs(sel, root) { return (root || document).querySelector(sel); }
|
||
function ce(tag, cls, txt) {
|
||
var n = document.createElement(tag);
|
||
if (cls) n.className = cls;
|
||
if (typeof txt !== 'undefined' && txt !== null) n.textContent = txt;
|
||
return n;
|
||
}
|
||
|
||
function log(msg) {
|
||
try { console.log('[p365x] ' + msg); } catch (e) {}
|
||
}
|
||
|
||
function safeUrl(url) {
|
||
if (!CFG.proxyPrefix) return url;
|
||
return CFG.proxyPrefix + encodeURIComponent(url);
|
||
}
|
||
|
||
function requestText(url) {
|
||
return new Promise(function (resolve, reject) {
|
||
try {
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open('GET', safeUrl(url), true);
|
||
xhr.timeout = CFG.requestTimeoutMs;
|
||
xhr.onreadystatechange = function () {
|
||
if (xhr.readyState !== 4) return;
|
||
if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.responseText || '');
|
||
else reject(new Error('HTTP ' + xhr.status));
|
||
};
|
||
xhr.ontimeout = function () { reject(new Error('TIMEOUT')); };
|
||
xhr.onerror = function () { reject(new Error('NETWORK')); };
|
||
xhr.send();
|
||
} catch (e) {
|
||
reject(e);
|
||
}
|
||
});
|
||
}
|
||
|
||
function extractCards(html) {
|
||
var out = [];
|
||
var re = /<li id="(\d+)"[\s\S]*?<a class="image" href="([^"]+)"[\s\S]*?<img src="([^"]+)"[\s\S]*?<p>([^<]+)<\/p>[\s\S]*?<span class="duration">([^<]*)<\/span>/g;
|
||
var m;
|
||
while ((m = re.exec(html))) {
|
||
out.push({
|
||
id: m[1],
|
||
url: m[2].indexOf('http') === 0 ? m[2] : (CFG.baseUrl + m[2]),
|
||
poster: m[3],
|
||
title: (m[4] || '').trim(),
|
||
duration: (m[5] || '').trim()
|
||
});
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function extractMp4(movieHtml) {
|
||
var all = [];
|
||
var re = /file\s*:\s*"([^"]+\.mp4[^"]*)"/g;
|
||
var m;
|
||
while ((m = re.exec(movieHtml))) all.push(m[1]);
|
||
|
||
if (!all.length) {
|
||
var v = movieHtml.match(/<video[^>]+src="([^"]+\.mp4[^"]*)"/i);
|
||
if (v) all.push(v[1]);
|
||
}
|
||
|
||
all.sort(function (a, b) {
|
||
function q(x) {
|
||
var mm = x.match(/_(\d{3,4})p\.mp4/i);
|
||
return mm ? Number(mm[1]) : 0;
|
||
}
|
||
return q(b) - q(a);
|
||
});
|
||
|
||
return all[0] || '';
|
||
}
|
||
|
||
function setStatus(text) {
|
||
var s = qs('#p365x-status');
|
||
if (s) s.textContent = text || '';
|
||
}
|
||
|
||
function makeCss() {
|
||
if (qs('#p365x-style')) return;
|
||
var css = ce('style');
|
||
css.id = 'p365x-style';
|
||
css.textContent = '' +
|
||
'#p365x-fab{position:fixed;right:24px;bottom:24px;z-index:99999;background:#e91e63;color:#fff;border:none;border-radius:999px;padding:10px 14px;font-weight:700;}' +
|
||
'#p365x-root{position:fixed;inset:0;z-index:99998;background:#111;color:#fff;display:none;}' +
|
||
'#p365x-head{padding:12px 16px;display:flex;gap:10px;align-items:center;border-bottom:1px solid #333;}' +
|
||
'#p365x-list{padding:10px;display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px;overflow:auto;height:calc(100% - 64px);}' +
|
||
'.p365x-card{background:#1b1b1b;border:1px solid #333;border-radius:8px;overflow:hidden;cursor:pointer;}' +
|
||
'.p365x-card img{width:100%;height:130px;object-fit:cover;display:block;}' +
|
||
'.p365x-meta{padding:8px;font-size:13px;}' +
|
||
'#p365x-player{position:fixed;inset:0;z-index:100000;background:#000;display:none;}' +
|
||
'#p365x-player video{width:100%;height:100%;}' +
|
||
'#p365x-close-player{position:absolute;top:10px;right:10px;z-index:2;}';
|
||
document.head.appendChild(css);
|
||
}
|
||
|
||
function renderItems() {
|
||
var list = qs('#p365x-list');
|
||
if (!list) return;
|
||
|
||
list.innerHTML = '';
|
||
STATE.items.forEach(function (item) {
|
||
var c = ce('div', 'p365x-card');
|
||
c.innerHTML = '<img src="' + item.poster + '" alt=""><div class="p365x-meta"><div>' + item.title + '</div><div style="opacity:.7">' + item.duration + '</div></div>';
|
||
c.onclick = function () {
|
||
setStatus('Загрузка видео...');
|
||
requestText(item.url)
|
||
.then(function (html) {
|
||
var src = extractMp4(html);
|
||
if (!src) throw new Error('Поток не найден');
|
||
var player = qs('#p365x-player');
|
||
var video = qs('#p365x-video');
|
||
video.src = src;
|
||
player.style.display = 'block';
|
||
setStatus('');
|
||
})
|
||
.catch(function (e) {
|
||
setStatus('Ошибка: ' + (e && e.message ? e.message : e));
|
||
});
|
||
};
|
||
list.appendChild(c);
|
||
});
|
||
}
|
||
|
||
function loadNextPage() {
|
||
if (STATE.loading) return;
|
||
STATE.loading = true;
|
||
|
||
var url = STATE.page > 1 ? (CFG.baseUrl + '/page/' + STATE.page + '/') : (CFG.baseUrl + '/');
|
||
|
||
setStatus('Загрузка страницы ' + STATE.page + '...');
|
||
requestText(url)
|
||
.then(function (html) {
|
||
var items = extractCards(html);
|
||
STATE.items = STATE.items.concat(items);
|
||
STATE.page += 1;
|
||
renderItems();
|
||
setStatus('Найдено: ' + STATE.items.length);
|
||
})
|
||
.catch(function (e) {
|
||
setStatus('Не удалось загрузить. CORS/блокировка: ' + (e && e.message ? e.message : e));
|
||
})
|
||
.finally(function () {
|
||
STATE.loading = false;
|
||
});
|
||
}
|
||
|
||
function openPanel() {
|
||
var root = qs('#p365x-root');
|
||
if (!root) return;
|
||
root.style.display = 'block';
|
||
if (!STATE.items.length) loadNextPage();
|
||
}
|
||
|
||
function mountUi() {
|
||
if (STATE.mounted || qs('#p365x-root')) return;
|
||
STATE.mounted = true;
|
||
|
||
makeCss();
|
||
|
||
var fab = ce('button');
|
||
fab.id = 'p365x-fab';
|
||
fab.textContent = 'P365';
|
||
fab.onclick = openPanel;
|
||
|
||
var root = ce('div');
|
||
root.id = 'p365x-root';
|
||
root.innerHTML = '<div id="p365x-head"><button id="p365x-close">Закрыть</button><button id="p365x-more">Ещё</button><span id="p365x-status"></span></div><div id="p365x-list"></div>';
|
||
|
||
var player = ce('div');
|
||
player.id = 'p365x-player';
|
||
player.innerHTML = '<button id="p365x-close-player">Закрыть</button><video id="p365x-video" controls autoplay playsinline></video>';
|
||
|
||
document.body.appendChild(fab);
|
||
document.body.appendChild(root);
|
||
document.body.appendChild(player);
|
||
|
||
qs('#p365x-close').onclick = function () { root.style.display = 'none'; };
|
||
qs('#p365x-more').onclick = loadNextPage;
|
||
qs('#p365x-close-player').onclick = function () {
|
||
player.style.display = 'none';
|
||
var v = qs('#p365x-video');
|
||
v.pause();
|
||
v.removeAttribute('src');
|
||
v.load();
|
||
};
|
||
|
||
addLampaMenuButton();
|
||
log('UI mounted');
|
||
}
|
||
|
||
function addLampaMenuButton() {
|
||
try {
|
||
if (!window.Lampa || !Lampa.SettingsApi || !Lampa.SettingsApi.addParam) return;
|
||
|
||
Lampa.SettingsApi.addParam({
|
||
component: 'interface',
|
||
param: {
|
||
name: 'p365x_open_btn',
|
||
type: 'button'
|
||
},
|
||
field: {
|
||
name: 'P365X каталог',
|
||
description: 'Открыть каталог P365X'
|
||
},
|
||
onChange: function () {
|
||
openPanel();
|
||
}
|
||
});
|
||
|
||
log('Settings button registered');
|
||
} catch (e) {
|
||
log('Settings button error: ' + (e && e.message ? e.message : e));
|
||
}
|
||
}
|
||
|
||
function start() {
|
||
try {
|
||
mountUi();
|
||
} catch (e) {
|
||
log('Start error: ' + (e && e.message ? e.message : e));
|
||
}
|
||
}
|
||
|
||
if (window.appready) {
|
||
start();
|
||
} else if (window.Lampa && Lampa.Listener && Lampa.Listener.follow) {
|
||
Lampa.Listener.follow('app', function (e) {
|
||
if (e && e.type === 'ready') start();
|
||
});
|
||
} else {
|
||
window.addEventListener('load', start);
|
||
}
|
||
})();
|