Исправление: том ./mods:/lampac/mods в compose и копии 10 модулей с enable:true; конфиг без SkipModules
This commit is contained in:
@@ -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);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user