(function () {
'use strict';
var lampac_host = '{localhost}';
var network = new Lampa.Reguest();
// Пользователя выбирает сервер, как в account() остальных плагинов Lampac: token,
// account_email и uid передаются отдельными параметрами, RequestInfo.getuid берёт первое
// непустое. Иначе подписки живут под номером устройства, а закладки и тайм-коды того же
// человека — под его аккаунтом.
function apiUrl(path) {
var url = lampac_host + path;
var ids = {
token: Lampa.Storage.get('token', ''),
account_email: Lampa.Storage.get('account_email', ''),
uid: Lampa.Storage.get('lampac_unic_id', '')
};
for (var name in ids) {
if (ids[name]) url = Lampa.Utils.addUrlComponent(url, name + '=' + encodeURIComponent(ids[name]));
}
return url;
}
function pad(n) { return n < 10 ? '0' + n : '' + n; }
var TMDB_KEY = '';
// TMDB image/api с поддержкой прокси — используем встроенные методы Lampa
function tmdbImage(url) {
try { return Lampa.TMDB.image(url); } catch(e) {}
return 'https://image.tmdb.org/' + url;
}
function tmdbApi(path) {
try { return Lampa.TMDB.api(path + '&api_key=' + (Lampa.TMDB.key ? Lampa.TMDB.key() : TMDB_KEY)); } catch(e) {}
return 'https://api.themoviedb.org/3/' + path + '&api_key=' + TMDB_KEY;
}
// =============================================
// Компонент подписок — построен по образцу
// src/interaction/items/category/base.js
// =============================================
function TgSubscribesComponent(object) {
var scroll = new Lampa.Scroll({ mask: true, over: true, step: 250, end_ratio: 2 });
var items = [];
var html = document.createElement('div');
var body = $(document.createElement('div')); // jQuery объект, как в Lampa
var last;
var active = 0;
this.create = function () {
var self = this;
// Как в base.js: сначала scroll.minus(), потом append body
scroll.minus();
scroll.append(body);
html.appendChild(scroll.render(true));
// Layer.visible при скролле — для ленивой загрузки изображений
scroll.onScroll = function () {
Lampa.Layer.visible(scroll.render(true));
};
self.activity.loader(true);
var net = new Lampa.Reguest();
net.timeout(15000);
net['native'](apiUrl('/api/tg/subscriptions'), function (result) {
if (!result.linked || !result.results || !result.results.length) {
self.empty();
return;
}
var subs = result.results;
var lang = Lampa.Storage.field('tmdb_lang') || 'ru-RU';
var total = subs.length;
var done = 0;
var tmdbData = {};
function buildCards() {
// Классы сетки из items.js: mapping--grid + cols--6
body.addClass('mapping--grid');
body.addClass('cols--6');
var frag = document.createDocumentFragment();
subs.forEach(function (sub) {
var t = tmdbData[sub.tmdb_id] || {};
var cardData = {
id: sub.tmdb_id,
tmdb_id: sub.tmdb_id,
tmdb: sub.tmdb_id,
name: t.name || sub.title,
title: t.name || sub.title,
original_name: t.original_name || sub.title,
poster_path: t.poster_path || '',
backdrop_path: t.backdrop_path || '',
vote_average: t.vote_average || 0,
first_air_date: t.first_air_date || '',
number_of_seasons: t.number_of_seasons || sub.last_season,
source: 'tmdb'
};
if (t.poster_path)
cardData.img = tmdbImage('t/p/w300' + t.poster_path);
var card = new Lampa.Card(cardData, { object: object, card_category: true });
card.create();
var season = parseInt(sub.last_season) || 0;
var episode = parseInt(sub.last_episode) || 0;
var voice = (sub.voice || '').trim();
// Бейдж подписки — без pad() как в стандартном Subscribe модуле Lampa
var badge = document.createElement('div');
badge.className = 'card__subscribe';
badge.innerHTML =
'
' +
'
S' + season + ' E' + episode + '
' +
'
' + (voice || 'Любая') + '
';
var view = card.render(true).querySelector('.card__view');
if (view) view.after(badge);
card.onFocus = function (target, data) {
last = target;
active = items.indexOf(card);
scroll.update($(target), true);
// Используем стандартную функцию Lampa — сама выбирает постер/backdrop по настройкам
Lampa.Background.change(Lampa.Utils.cardImgBackground(data));
};
card.onEnter = function (target, data) {
Lampa.Activity.push({
url: '', component: 'full',
id: data.tmdb_id || data.id,
method: 'tv', card: data, source: 'tmdb'
});
};
frag.appendChild(card.render(true));
items.push(card);
});
body.append(frag);
// Устанавливаем фон на первую карточку при открытии
if (items.length > 0) {
Lampa.Background.change(Lampa.Utils.cardImgBackground(items[0].data));
}
// Layer.visible — запускает ленивую загрузку изображений в карточках
Lampa.Layer.visible(scroll.render(true));
self.activity.loader(false);
self.activity.toggle();
}
// Параллельная загрузка TMDB через Lampa.Network (с кешем и прокси)
subs.forEach(function (sub) {
var url = tmdbApi('tv/' + sub.tmdb_id + '?language=' + lang);
Lampa.Network.silent(url, function (t) {
if (t && !t.status_code) tmdbData[sub.tmdb_id] = t;
if (++done >= total) buildCards();
},
function () { if (++done >= total) buildCards(); },
false, { cache: { life: 60 * 24 } }
);
});
}, function () { self.empty(); });
};
this.empty = function () {
var empty = new Lampa.Empty();
html.appendChild(empty.render(true));
this.start = empty.start.bind(empty);
this.activity.loader(false);
this.activity.toggle();
};
// start() — точная копия base.js
this.start = function () {
var self = this;
Lampa.Controller.add('content', {
link: self,
invisible: true,
toggle: function () {
scroll.restorePosition ? scroll.restorePosition() : null;
Lampa.Controller.collectionSet(scroll.render(true));
Lampa.Controller.collectionFocus(last || false, scroll.render(true));
},
left: function () { if (Navigator.canmove('left')) Navigator.move('left'); else Lampa.Controller.toggle('menu'); },
right: function () { if (Navigator.canmove('right')) Navigator.move('right'); },
up: function () { if (Navigator.canmove('up')) Navigator.move('up'); else Lampa.Controller.toggle('head'); },
down: function () { if (Navigator.canmove('down')) Navigator.move('down'); },
back: function () { Lampa.Activity.backward(); }
});
Lampa.Controller.toggle('content');
};
this.pause = function () {};
this.stop = function () {};
this.render = function (js) { return js ? html : $(html); };
this.destroy = function () {
items.forEach(function (c) { try { c.destroy(); } catch(e) {} });
scroll.destroy();
html.remove();
items = [];
};
}
// =============================================
// Меню
// =============================================
var MENU_ICON = '';
var MENU_TITLE = 'TG Подписки';
var MENU_ACTION = 'tg_subscribes';
function openSubscribesPage() {
Lampa.Activity.push({ url: '', title: MENU_TITLE, component: MENU_ACTION, page: 1 });
}
function addMenu() {
if ($('.menu__item[data-action="' + MENU_ACTION + '"]').length) return;
Lampa.Menu.addButton(MENU_ICON, MENU_TITLE, openSubscribesPage).attr('data-action', MENU_ACTION);
try {
var sort = Lampa.Storage.get('menu_sort', '[]');
if (typeof sort === 'string') sort = JSON.parse(sort);
if (Array.isArray(sort) && sort.indexOf(MENU_TITLE) === -1) {
sort.push(MENU_TITLE);
Lampa.Storage.set('menu_sort', JSON.stringify(sort));
}
} catch(e) {}
}
function registerComponent() {
Lampa.Component.add(MENU_ACTION, TgSubscribesComponent);
setTimeout(addMenu, 500);
}
// =============================================
// Перехват кнопки 🔔 в карточке сериала
// =============================================
function initSubscribeOverride() {
Lampa.Listener.follow('full', function (e) {
if (e.type === 'complite')
setTimeout(function () { overrideSubscribeButton(e.object); }, 300);
});
}
function overrideSubscribeButton(obj) {
if (!obj || !obj.card || !obj.card.number_of_seasons) return;
var card = obj.card;
var button;
try { button = obj.activity.render().find('.button--subscribe'); } catch(e) { return; }
if (!button || !button.length) return;
button.removeClass('hide');
button.off('hover:enter');
checkStatus(card.id, function (status) {
if (status.subscribed) button.addClass('active').find('path').attr('fill', 'currentColor');
button.on('hover:enter', function () {
if (!status.linked) showLinkDialog();
else showVoiceMenu(card, button, status);
});
});
}
function showVoiceMenu(card, button, currentStatus) {
var title = card.name || card.title || '';
var year = card.first_air_date ? parseInt(card.first_air_date) : (card.year || 0);
var tmdbId = card.id || 0;
Lampa.Loading.start(function () { network.clear(); Lampa.Loading.stop(); });
network.clear();
network.timeout(30000);
network['native'](
apiUrl('/api/tg/voices?title=' + encodeURIComponent(title) + '&year=' + year + '&season=0&tmdb_id=' + tmdbId),
function (result) {
Lampa.Loading.stop();
var season = result.season || card.number_of_seasons || 1;
var items = [];
if (currentStatus && currentStatus.subscribed && currentStatus.voices) {
currentStatus.voices.forEach(function (v) {
items.push({ title: '❌ Отписаться: ' + (v || 'Любая озвучка'), unsubscribe: true, voice: v });
});
if (items.length) items.push({ title: '', separator: true });
}
items.push({ title: '🔔 Любая озвучка (оригинал)', subtitle: 'При выходе серии', voice: '', orid: '', voice_id: 0, collaps_orid: '', voice_source: '' });
if (result.success && result.voices && result.voices.length) {
items.push({ title: 'Озвучки:', separator: true });
result.voices.forEach(function (v) {
var src = v.source || 'mirage';
var badge = src === 'collaps' ? ' [C]' : src === 'rhs' ? ' [R]' : src === 'videohub' ? ' [V]' : '';
items.push({ title: '🎙 ' + v.name + badge, voice: v.name, orid: result.orid || '', voice_id: v.id || 0, collaps_orid: result.collaps_orid || '', kp_id: result.kp_id || 0, voice_source: src });
});
}
Lampa.Select.show({
title: 'TG Уведомления', items: items,
onSelect: function (a) {
Lampa.Controller.toggle('content');
if (a.unsubscribe) doUnsubscribe(card, button, a.voice);
else if (typeof a.voice !== 'undefined') doSubscribe(card, a.voice, season, button, a.orid, a.voice_id, a.collaps_orid, a.voice_source, a.kp_id);
},
onBack: function () { Lampa.Controller.toggle('content'); }
});
},
function () {
Lampa.Loading.stop();
var items = [{ title: '🔔 Подписаться (без озвучки)', voice: '', orid: '', voice_id: 0 }];
if (currentStatus && currentStatus.subscribed) items.unshift({ title: '❌ Отписаться', unsubscribe: true, voice: '' });
Lampa.Select.show({
title: 'TG Уведомления', items: items,
onSelect: function (a) {
Lampa.Controller.toggle('content');
if (a.unsubscribe) doUnsubscribe(card, button, null);
else doSubscribe(card, '', card.number_of_seasons || 1, button, '', 0);
},
onBack: function () { Lampa.Controller.toggle('content'); }
});
}
);
}
function doSubscribe(card, voice, season, button, orid, voiceId, collapsOrid, voiceSource, kpId) {
network.clear();
network['native'](apiUrl('/api/tg/subscribe'), function (r) {
if (r.success) {
Lampa.Noty.show(voice ? '🔔 Подписка: ' + voice : '🔔 Подписка оформлена!');
button.addClass('active').find('path').attr('fill', 'currentColor');
} else if (r.msg === 'not_linked') {
showLinkDialog();
} else {
Lampa.Noty.show('Ошибка: ' + (r.msg || ''));
}
}, function () {
Lampa.Noty.show('Ошибка подписки');
}, JSON.stringify({ tmdb_id: card.id, title: card.name || card.title || '', voice: voice, season: season, episode: 0, mirage_orid: orid || '', mirage_voice_id: voiceId || 0, voice_episode: 0, collaps_orid: collapsOrid || '', voice_source: voiceSource || '', kp_id: (card.kinopoisk_id || card.kp_id || kpId || 0) }),
{ dataType: 'json', contentType: 'application/json' });
}
function doUnsubscribe(card, button, voice) {
network.clear();
network['native'](apiUrl('/api/tg/unsubscribe'), function (r) {
if (r.success) {
Lampa.Noty.show('Подписка отменена');
checkStatus(card.id, function (st) {
if (!st.subscribed) button.removeClass('active').find('path').attr('fill', 'transparent');
});
}
}, function () { Lampa.Noty.show('Ошибка'); },
JSON.stringify({ tmdb_id: card.id, voice: voice }),
{ dataType: 'json', contentType: 'application/json' });
}
function checkStatus(tmdb_id, callback) {
var n = new Lampa.Reguest();
n.timeout(5000);
n['native'](apiUrl('/api/tg/status?tmdb_id=' + tmdb_id),
function (r) { callback(r || { success: false, linked: false, subscribed: false, voices: [] }); },
function () { callback({ success: false, linked: false, subscribed: false, voices: [] }); }
);
}
function showLinkDialog() {
var n = new Lampa.Reguest();
n['native'](apiUrl('/api/tg/link'), function (r) {
if (!r.success || !r.link) { Lampa.Noty.show('Бот не запущен'); return; }
Lampa.Select.show({
title: 'Привязка Telegram',
items: [
{ title: 'Открыть ссылку', subtitle: r.link, link: r.link },
{ title: 'Скопировать ссылку', show_link: r.link }
],
onSelect: function (a) {
Lampa.Controller.toggle('content');
if (a.link) {
if (typeof Android !== 'undefined' && Android.openBrowser) Android.openBrowser(a.link);
else if (window.open) window.open(a.link, '_blank');
}
if (a.show_link) Lampa.Noty.show(a.show_link);
},
onBack: function () { Lampa.Controller.toggle('content'); }
});
}, function () { Lampa.Noty.show('Модуль TelegramBot недоступен'); });
}
// =============================================
// Init
// =============================================
function init() {
registerComponent();
initSubscribeOverride();
console.log('[TG-Notify] Plugin loaded v17');
}
if (window.appready) init();
else Lampa.Listener.follow('app', function (e) { if (e.type === 'ready') init(); });
})();