Исправление: том ./mods:/lampac/mods в compose и копии 10 модулей с enable:true; конфиг без SkipModules

This commit is contained in:
2026-10-02 12:11:49 +03:00
parent 8dcbc6bc52
commit 7b8e28b3bf
151 changed files with 68625 additions and 44 deletions
+356
View File
@@ -0,0 +1,356 @@
(function () {
var API_BASE = '{localhost}';
var activeJob = null;
var heartbeatTimer = null;
var waitHandle = null;
function account(url) {
url = url + '';
if (url.indexOf('account_email=') == -1) {
var email = Lampa.Storage.get('account_email');
if (email) url = Lampa.Utils.addUrlComponent(url, 'account_email=' + encodeURIComponent(email));
}
if (url.indexOf('uid=') == -1) {
var uid = Lampa.Storage.get('lampac_unic_id', '');
if (uid) url = Lampa.Utils.addUrlComponent(url, 'uid=' + encodeURIComponent(uid));
}
if (url.indexOf('token=') == -1) {
var token = '{token}';
if (token != '') url = Lampa.Utils.addUrlComponent(url, 'token={token}');
}
return url;
}
function showWait(message) {
Lampa.Loading.start(function(){}, message);
}
function hideWait() {
Lampa.Loading.stop();
}
function log() {
try {
var args = Array.prototype.slice.call(arguments);
args.unshift('Transcoder');
console.log.apply(console, args);
}
catch (err) {
/* noop */
}
}
function notify(message) {
if (window.Lampa && Lampa.Noty && Lampa.Noty.show) {
Lampa.Noty.show(message);
}
else {
log(message);
}
}
function resolveMediaUrl(data) {
if (data && data.url) return data.url;
return '';
}
function isMkvSource(data) {
var url = resolveMediaUrl(data);
if (!url) return false;
return /\.(mkv|avi|flv)($|\?|#)/i.test(url) || /\/lite\/pidtor\//i.test(url);
}
function requestFfprobe(mediaUrl, onSuccess, onError) {
if (!mediaUrl) {
if (onError) onError(new Error('Empty media url'));
return;
}
var net = new Lampa.Reguest();
var target = account(API_BASE + '/ffprobe?media=' + encodeURIComponent(mediaUrl));
log('request ffprobe', target);
net.native(target, function (response) {
try {
var json = typeof response === 'string' ? JSON.parse(response) : response;
try {
var streams = json && Array.isArray(json.streams) ? json.streams : [];
var v = streams.find(function(s){ return s.codec_type === 'video'; });
var rawCodec = (v && (v.codec_name || (v.tags && v.tags.codec_name))) || '';
} catch(e) { /* noop */ }
if (onSuccess) onSuccess(json || {});
} catch(e) {
if (onError) onError(e);
}
}, function (error) {
hideWait();
if (onError) onError(error || new Error('ffprobe request failed'));
}, null, {
dataType: 'text',
timeout: 1000 * 40
});
}
function formatAudioItem(track, index) {
var tags = track && track.tags ? track.tags : {};
var title = tags.title || tags.handler_name || ('Audio #' + (index + 1));
var lang = (tags.language || '').toUpperCase();
var codec = (track.codec_name || '').toUpperCase();
var channels = (track.channel_layout || '').replace('(side)', '').replace('stereo', '2.0');
var rate = track.bit_rate ? Math.round(track.bit_rate / 1000) + ' kbps' : '';
var subtitleParts = [];
if (lang) subtitleParts.push(lang);
if (codec) subtitleParts.push(codec);
if (channels) subtitleParts.push(channels);
if (rate) subtitleParts.push(rate);
return {
title: title,
subtitle: subtitleParts.join(' • '),
track: track
};
}
function selectAutoAudioTrack(audioTracks) {
for (var i = 0; i < audioTracks.length; i++) {
var disposition = audioTracks[i] && audioTracks[i].disposition;
if (disposition && (disposition.default === 1 || disposition.default === true)) {
return audioTracks[i];
}
}
var language = String(Lampa.Storage.get('language', 'ru') || 'ru').toLowerCase().split('-')[0];
var aliases = {
ru: ['ru', 'rus'],
en: ['en', 'eng'],
uk: ['uk', 'ukr'],
be: ['be', 'bel'],
bg: ['bg', 'bul'],
he: ['he', 'heb'],
cs: ['cs', 'ces', 'cze'],
ro: ['ro', 'ron', 'rum'],
de: ['de', 'deu', 'ger'],
fr: ['fr', 'fra', 'fre'],
es: ['es', 'spa'],
it: ['it', 'ita'],
pt: ['pt', 'por'],
pl: ['pl', 'pol'],
tr: ['tr', 'tur'],
ja: ['ja', 'jpn'],
ko: ['ko', 'kor'],
zh: ['zh', 'zho', 'chi']
};
var preferred = aliases[language] || (language ? [language] : []);
for (var j = 0; j < audioTracks.length; j++) {
var tags = audioTracks[j] && audioTracks[j].tags ? audioTracks[j].tags : {};
var trackLanguage = String(tags.language || '').toLowerCase();
if (preferred.indexOf(trackLanguage) !== -1) {
return audioTracks[j];
}
}
return audioTracks[0];
}
function showAudioSelector(data, audioTracks) {
if (!audioTracks.length) {
notify('Не удалось найти аудиодорожки для MKV файла');
return;
}
var manualSelection = Lampa.Storage.get('transcoding_audio_prompt', false);
if (!(manualSelection === true || manualSelection === 'true')) {
startTranscoding(data, selectAutoAudioTrack(audioTracks));
return;
}
var items = audioTracks.map(function (track, index) {
return formatAudioItem(track, index);
});
var last_controller = Lampa.Controller.enabled().name
Lampa.Select.show({
title: 'Выберите аудиодорожку',
items: items,
onSelect: function (item) {
Lampa.Select.close();
if (!item || !item.track) {
notify('Выбрана пустая дорожка');
return;
}
startTranscoding(data, item.track);
},
onBack: function () {
Lampa.Controller.toggle(last_controller)
}
});
}
function startTranscoding(data, track) {
stopHeartbeat();
ensureJobStopped(true);
var payload = {
src: resolveMediaUrl(data),
audio: { index: track.index -1 },
live: false
};
/*if (data && data.subtitles) {
payload.subtitles = !!data.subtitles;
}*/
var net = new Lampa.Reguest();
net.native(account(API_BASE + '/transcoding/start'), function (response) {
var json = typeof response === 'string' ? JSON.parse(response) : response;
if (!json || !json.streamId || !json.playlistUrl) {
notify('Некорректный ответ от сервера перекодирования');
log('invalid start response', response);
return;
}
activeJob = {
streamId: json.streamId,
playlistUrl: json.playlistUrl
};
var playback = data || {};
playback.transcoding = true;
playback.ffprobe = null;
playback.url = json.playlistUrl;
playback.subtitles_call = json.subtitlesUrl;
playback.hls_manifest_timeout = json.hls_timeout_seconds * 1000;
Lampa.Player.play(playback);
}, function (error) {
hideWait();
notify('Не удалось запустить перекодирование MKV');
log('start error', error);
}, JSON.stringify(payload), {
dataType: 'json',
type: 'POST',
timeout: 1000 * 20,
headers: {
'Content-Type': 'application/json'
}
});
}
function sendHeartbeat() {
if (!activeJob) return;
var net = new Lampa.Reguest();
net.native(account(API_BASE + '/transcoding/' + activeJob.streamId + '/heartbeat'), function () {
log('heartbeat sent');
}, function (error) {
hideWait();
log('heartbeat error', error);
}, null, {
dataType: 'text',
timeout: 1000 * 10
});
}
function stopHeartbeat() {
if (heartbeatTimer) {
clearInterval(heartbeatTimer);
heartbeatTimer = null;
}
}
function ensureJobStopped(sendRemote) {
if (!activeJob) return;
var job = activeJob;
activeJob = null;
stopHeartbeat();
if (sendRemote) {
var net = new Lampa.Reguest();
net.native(account(API_BASE + '/transcoding/' + job.streamId + '/stop'), function () {
log('stop sent');
}, function (error) {
hideWait();
log('stop error', error);
}, null, {
dataType: 'text',
timeout: 1000 * 10
});
}
}
function registerSettings() {
if (window.lampac_transcoding_audio_setting || !Lampa.SettingsApi || !Lampa.SettingsApi.addParam) return;
window.lampac_transcoding_audio_setting = true;
Lampa.SettingsApi.addParam({
component: 'player',
param: {
name: 'transcoding_audio_prompt',
type: 'trigger',
values: '',
default: false
},
field: {
name: 'Спрашивать аудиодорожку при транскодировании',
description: 'Включите для ручного выбора дорожки перед запуском MKV'
}
});
}
function handlePlayerStart(e) {
if (!isMkvSource(e.data) || e.data.transcoding || /\/transcoding\//i.test(e.data.url)) return;
e.abort();
e.data.url = e.data.url.replace(/&(preload|stat|m3u)/g, '&play');
log('intercept mkv playback', e.data.url);
showWait('Получение списка аудио дорожек...');
requestFfprobe(resolveMediaUrl(e.data), function (info) {
hideWait();
var streams = info && Array.isArray(info.streams) ? info.streams : [];
var audioTracks = streams.filter(function (track) {
return track.codec_type === 'audio';
});
showAudioSelector(e.data, audioTracks);
}, function (error) {
hideWait();
notify('Ошибка анализа MKV файла');
log('ffprobe error', error);
});
}
function handlePlayerDestroy() {
ensureJobStopped(true);
}
function handleVideoPause() {
if (!activeJob) return;
stopHeartbeat();
heartbeatTimer = setInterval(sendHeartbeat, 1000 * 10);
}
function handleVideoPlay() {
if (!activeJob) return;
stopHeartbeat();
}
if (!window.lampac_transcoding_plugin) {
window.lampac_transcoding_plugin = true;
registerSettings();
Lampa.Player.listener.follow('create', handlePlayerStart);
Lampa.Player.listener.follow('destroy', handlePlayerDestroy);
Lampa.PlayerVideo.listener.follow('pause', handleVideoPause);
Lampa.PlayerVideo.listener.follow('play', handleVideoPlay);
}
})();