Files
porn365/porn365.js

259 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(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);
}
})();