(function () { 'use strict'; if (!Object.assign) { Object.defineProperty(Object, 'assign', { enumerable: false, configurable: true, writable: true, value: function value(target, firstSource) { if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object'); } var to = Object(target); for (var i = 1; i < arguments.length; i++) { var nextSource = arguments[i]; if (nextSource === undefined || nextSource === null) { continue; } var keysArray = Object.keys(Object(nextSource)); for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { var nextKey = keysArray[nextIndex]; var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined && desc.enumerable) { to[nextKey] = nextSource[nextKey]; } } } return to; } }); } if (!('remove' in Element.prototype)) { Element.prototype.remove = function () { if (this.parentNode) this.parentNode.removeChild(this); }; } if (!('find' in Element.prototype)) { Element.prototype.find = function (query) { return this.querySelector(query); }; } if (!('text' in Element.prototype)) { Element.prototype.text = function (text) { this.innerText = text; return this; }; } if (!('html' in Element.prototype)) { Element.prototype.html = function (html) { if (typeof html == 'string') this.innerHTML = html;else { this.innerHTML = ''; this.append(html); } return this; }; } if (!('after' in Element.prototype)) { Element.prototype.after = function (newElem) { this.parentNode.insertBefore(newElem instanceof jQuery ? newElem[0] : newElem, this.nextSibling); return this; }; } if (!('removeClass' in Element.prototype)) { Element.prototype.removeClass = function (classes) { var _this = this; classes.split(' ').forEach(function (c) { _this.classList.remove(c); }); return this; }; } if (!('addClass' in Element.prototype)) { Element.prototype.addClass = function (classes) { var _this2 = this; classes.split(' ').forEach(function (c) { _this2.classList.add(c); }); return this; }; } if (!('toggleClass' in Element.prototype)) { Element.prototype.toggleClass = function (classes, status) { var _this3 = this; classes.split(' ').forEach(function (c) { var has = _this3.classList.contains(c); if (status && !has) _this3.classList.add(c);else if (!status && has) _this3.classList.remove(c); }); return this; }; } if (!('replaceWith' in Element.prototype)) { Element.prototype.replaceWith = function (newElem) { if (this.parentNode) this.parentNode.replaceChild(newElem instanceof jQuery ? newElem[0] : newElem, this); return this; }; } if (!('empty' in Element.prototype)) { Element.prototype.empty = function () { this.innerHTML = ''; return this; }; } Element.prototype.append = function (child) { var _this4 = this; if (Object.prototype.toString.call(child) === '[object Array]') { child.forEach(function (c) { _this4.appendChild(c instanceof jQuery ? c[0] : c); }); } else this.appendChild(child instanceof jQuery ? child[0] : child); return this; }; Element.prototype.prepend = function (child) { var _this5 = this; if (Object.prototype.toString.call(child) === '[object Array]') { child.forEach(function (c) { _this5.insertBefore(c instanceof jQuery ? c[0] : c, _this5.firstChild); }); } else this.insertBefore(child instanceof jQuery ? child[0] : child, this.firstChild); return this; }; Element.prototype.hasClass = function (className) { return this.classList.contains(className); }; if (!('on' in Element.prototype)) { Element.prototype.on = function (on, call, options) { var _this6 = this; on.split(' ').forEach(function (e) { _this6.addEventListener(e, call, options); }); return this; }; } Number.prototype.pad = function (zeros) { var numberString = String(this); var zerosToAdd = zeros - numberString.length; var leadingZeros = ""; for (var i = 0; i < zerosToAdd; i++) { leadingZeros += "0"; } return leadingZeros + numberString; }; if (!Math.trunc) { Math.trunc = function (v) { v = +v; return v - v % 1 || (!isFinite(v) || v === 0 ? v : v < 0 ? -0 : 0); }; } // Polyfill для requestAnimationFrame window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { return setTimeout(function () { callback(Date.now()); }, 1000 / 60); }; if (!Array.from) { Array.from = function () { var toStr = Object.prototype.toString; var isCallable = function isCallable(fn) { return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; }; var toInteger = function toInteger(value) { var number = Number(value); if (isNaN(number)) { return 0; } if (number === 0 || !isFinite(number)) { return number; } return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); }; var maxSafeInteger = Math.pow(2, 53) - 1; var toLength = function toLength(value) { var len = toInteger(value); return Math.min(Math.max(len, 0), maxSafeInteger); }; // Свойство length метода from равно 1. return function from(arrayLike /* , mapFn, thisArg */ ) { // 1. Положим C равным значению this. var C = this; // 2. Положим items равным ToObject(arrayLike). var items = Object(arrayLike); // 3. ReturnIfAbrupt(items). if (arrayLike == null) { throw new TypeError('Array.from requires an array-like object - not null or undefined'); } // 4. Если mapfn равен undefined, положим mapping равным false. var mapFn = arguments.length > 1 ? arguments[1] : void undefined; var T; if (typeof mapFn !== 'undefined') { // 5. иначе // 5. a. Если вызов IsCallable(mapfn) равен false, выкидываем // исключение TypeError. if (!isCallable(mapFn)) { throw new TypeError('Array.from: when provided, the second argument must be a function'); } // 5. b. Если thisArg присутствует, положим T равным thisArg; // иначе положим T равным undefined. if (arguments.length > 2) { T = arguments[2]; } } // 10. Положим lenValue равным Get(items, "length"). // 11. Положим len равным ToLength(lenValue). var len = toLength(items.length); // 13. Если IsConstructor(C) равен true, то // 13. a. Положим A равным результату вызова внутреннего метода // [[Construct]] // объекта C со списком аргументов, содержащим единственный элемент // len. // 14. a. Иначе, положим A равным ArrayCreate(len). var A = isCallable(C) ? Object(new C(len)) : new Array(len); // 16. Положим k равным 0. var k = 0; // 17. Пока k < len, будем повторять... (шаги с a по h) var kValue; while (k < len) { kValue = items[k]; if (mapFn) { A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); } else { A[k] = kValue; } k += 1; } // 18. Положим putStatus равным Put(A, "length", len, true). A.length = len; // 20. Вернём A. return A; }; }(); } // https://tc39.github.io/ecma262/#sec-array.prototype.find if (!Array.prototype.find) { Object.defineProperty(Array.prototype, 'find', { value: function value(predicate) { // 1. Let O be ? ToObject(this value). if (this == null) { throw new TypeError('"this" is null or not defined'); } var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If IsCallable(predicate) is false, throw a TypeError // exception. if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } // 4. If thisArg was supplied, let T be thisArg; else let T be // undefined. var thisArg = arguments[1]; // 5. Let k be 0. var k = 0; // 6. Repeat, while k < len while (k < len) { // a. Let Pk be ! ToString(k). // b. Let kValue be ? Get(O, Pk). // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, // k, O »)). // d. If testResult is true, return kValue. var kValue = o[k]; if (predicate.call(thisArg, kValue, k, o)) { return kValue; } // e. Increase k by 1. k++; } // 7. Return undefined. return undefined; }, configurable: true, writable: true }); } // https://tc39.github.io/ecma262/#sec-array.prototype.includes if (!Array.prototype.includes) { Object.defineProperty(Array.prototype, 'includes', { value: function value(searchElement, fromIndex) { if (this == null) { throw new TypeError('"this" is null or not defined'); } // 1. Let O be ? ToObject(this value). var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If len is 0, return false. if (len === 0) { return false; } // 4. Let n be ? ToInteger(fromIndex). // (If fromIndex is undefined, this step produces the value 0.) var n = fromIndex | 0; // 5. If n ≥ 0, then // a. Let k be n. // 6. Else n < 0, // a. Let k be len + n. // b. If k < 0, let k be 0. var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); function sameValueZero(x, y) { return x === y || typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y); } // 7. Repeat, while k < len while (k < len) { // a. Let elementK be the result of ? Get(O, ! ToString(k)). // b. If SameValueZero(searchElement, elementK) is true, return // true. if (sameValueZero(o[k], searchElement)) { return true; } // c. Increase k by 1. k++; } // 8. Return false return false; } }); } if (!String.prototype.includes) { String.prototype.includes = function (search, start) { if (search instanceof RegExp) { throw TypeError('first argument must not be a RegExp'); } if (start === undefined) { start = 0; } return this.indexOf(search, start) !== -1; }; } if (!Object.entries) { Object.entries = function (obj) { var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array while (i--) { resArray[i] = [ownProps[i], obj[ownProps[i]]]; } return resArray; }; } if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== 'function') { // ближайший аналог внутренней функции // IsCallable в ECMAScript 5 throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function fNOP() {}, fBound = function fBound() { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; } (function () { var _slice = Array.prototype.slice; try { // Не может использоваться с элементами DOM в IE < 9 _slice.call(document.documentElement); } catch (e) { // В IE < 9 кидается исключение // Функция будет работать для истинных массивов, массивоподобных объектов, // NamedNodeMap (атрибуты, сущности, примечания), // NodeList (например, getElementsByTagName), HTMLCollection (например, childNodes) // и не будет падать на других объектах DOM (как это происходит на элементах DOM в IE < 9) Array.prototype.slice = function (begin, end) { // IE < 9 будет недоволен аргументом end, равным undefined end = typeof end !== 'undefined' ? end : this.length; // Для родных объектов Array мы используем родную функцию slice if (Object.prototype.toString.call(this) === '[object Array]') { return _slice.call(this, begin, end); } // Массивоподобные объекты мы обрабатываем самостоятельно var i, cloned = [], size, len = this.length; // Обрабатываем отрицательное значение begin var start = begin || 0; start = start >= 0 ? start : len + start; // Обрабатываем отрицательное значение end var upTo = end ? end : len; if (end < 0) { upTo = len + end; } // Фактически ожидаемый размер среза size = upTo - start; if (size > 0) { cloned = new Array(size); if (this.charAt) { for (i = 0; i < size; i++) { cloned[i] = this.charAt(start + i); } } else { for (i = 0; i < size; i++) { cloned[i] = this[start + i]; } } } return cloned; }; } })(); /*! * Shim for MutationObserver interface * Author: Graeme Yeates (github.com/megawac) * Repository: https://github.com/megawac/MutationObserver.js * License: WTFPL V2, 2004 (wtfpl.net). * Though credit and staring the repo will make me feel pretty, you can modify and redistribute as you please. * Attempts to follow spec (https://www.w3.org/TR/dom/#mutation-observers) as closely as possible for native javascript * See https://github.com/WebKit/webkit/blob/master/Source/WebCore/dom/MutationObserver.cpp for current webkit source c++ implementation */ /** * prefix bugs: - https://bugs.webkit.org/show_bug.cgi?id=85161 - https://bugzilla.mozilla.org/show_bug.cgi?id=749920 * Don't use WebKitMutationObserver as Safari (6.0.5-6.1) use a buggy implementation */ if (!window.MutationObserver) { window.MutationObserver = function (undefined$1) { /** * @param {function(Array., MutationObserver)} listener * @constructor */ function MutationObserver(listener) { /** * @type {Array.} * @private */ this._watched = []; /** @private */ this._listener = listener; } /** * Start a recursive timeout function to check all items being observed for mutations * @type {MutationObserver} observer * @private */ function startMutationChecker(observer) { (function check() { var mutations = observer.takeRecords(); if (mutations.length) { // fire away // calling the listener with context is not spec but currently consistent with FF and WebKit observer._listener(mutations, observer); } /** @private */ observer._timeout = setTimeout(check, MutationObserver._period); })(); } /** * Period to check for mutations (~32 times/sec) * @type {number} * @expose */ MutationObserver._period = 30 /*ms+runtime*/ ; /** * Exposed API * @expose * @final */ MutationObserver.prototype = { /** * see https://dom.spec.whatwg.org/#dom-mutationobserver-observe * not going to throw here but going to follow the current spec config sets * @param {Node|null} $target * @param {Object|null} config : MutationObserverInit configuration dictionary * @expose * @return undefined */ observe: function observe($target, config) { /** * Using slightly different names so closure can go ham * @type {!Object} : A custom mutation config */ var settings = { attr: !!(config.attributes || config.attributeFilter || config.attributeOldValue), // some browsers enforce that subtree must be set with childList, attributes or characterData. // We don't care as spec doesn't specify this rule. kids: !!config.childList, descendents: !!config.subtree, charData: !!(config.characterData || config.characterDataOldValue) }; var watched = this._watched; // remove already observed target element from pool for (var i = 0; i < watched.length; i++) { if (watched[i].tar === $target) watched.splice(i, 1); } if (config.attributeFilter) { /** * converts to a {key: true} dict for faster lookup * @type {Object.} */ settings.afilter = reduce(config.attributeFilter, function (a, b) { a[b] = true; return a; }, {}); } watched.push({ tar: $target, fn: createMutationSearcher($target, settings) }); // reconnect if not connected if (!this._timeout) { startMutationChecker(this); } }, /** * Finds mutations since last check and empties the "record queue" i.e. mutations will only be found once * @expose * @return {Array.} */ takeRecords: function takeRecords() { var mutations = []; var watched = this._watched; for (var i = 0; i < watched.length; i++) { watched[i].fn(mutations); } return mutations; }, /** * @expose * @return undefined */ disconnect: function disconnect() { this._watched = []; // clear the stuff being observed clearTimeout(this._timeout); // ready for garbage collection /** @private */ this._timeout = null; } }; /** * Simple MutationRecord pseudoclass. No longer exposing as its not fully compliant * @param {Object} data * @return {Object} a MutationRecord */ function MutationRecord(data) { var settings = { // technically these should be on proto so hasOwnProperty will return false for non explicitly props type: null, target: null, addedNodes: [], removedNodes: [], previousSibling: null, nextSibling: null, attributeName: null, attributeNamespace: null, oldValue: null }; for (var prop in data) { if (has(settings, prop) && data[prop] !== undefined$1) settings[prop] = data[prop]; } return settings; } /** * Creates a func to find all the mutations * * @param {Node} $target * @param {!Object} config : A custom mutation config */ function createMutationSearcher($target, config) { /** type {Elestuct} */ var $oldstate = clone($target, config); // create the cloned datastructure /** * consumes array of mutations we can push to * * @param {Array.} mutations */ return function (mutations) { var olen = mutations.length, dirty; if (config.charData && $target.nodeType === 3 && $target.nodeValue !== $oldstate.charData) { mutations.push(new MutationRecord({ type: "characterData", target: $target, oldValue: $oldstate.charData })); } // Alright we check base level changes in attributes... easy if (config.attr && $oldstate.attr) { findAttributeMutations(mutations, $target, $oldstate.attr, config.afilter); } // check childlist or subtree for mutations if (config.kids || config.descendents) { dirty = searchSubtree(mutations, $target, $oldstate, config); } // reclone data structure if theres changes if (dirty || mutations.length !== olen) { /** type {Elestuct} */ $oldstate = clone($target, config); } }; } /* attributes + attributeFilter helpers */ // Check if the environment has the attribute bug (#4) which cause // element.attributes.style to always be null. var hasAttributeBug = document.createElement("i"); hasAttributeBug.style.top = 0; hasAttributeBug = hasAttributeBug.attributes.style.value != "null"; /** * Gets an attribute value in an environment without attribute bug * * @param {Node} el * @param {Attr} attr * @return {String} an attribute value */ function getAttributeSimple(el, attr) { // There is a potential for a warning to occur here if the attribute is a // custom attribute in IE<9 with a custom .toString() method. This is // just a warning and doesn't affect execution (see #21) return attr.value; } /** * Gets an attribute value with special hack for style attribute (see #4) * * @param {Node} el * @param {Attr} attr * @return {String} an attribute value */ function getAttributeWithStyleHack(el, attr) { // As with getAttributeSimple there is a potential warning for custom attribtues in IE7. return attr.name !== "style" ? attr.value : el.style.cssText; } var getAttributeValue = hasAttributeBug ? getAttributeSimple : getAttributeWithStyleHack; /** * fast helper to check to see if attributes object of an element has changed * doesnt handle the textnode case * * @param {Array.} mutations * @param {Node} $target * @param {Object.} $oldstate : Custom attribute clone data structure from clone * @param {Object} filter */ function findAttributeMutations(mutations, $target, $oldstate, filter) { var checked = {}; var attributes = $target.attributes; var attr; var name; var i = attributes.length; while (i--) { attr = attributes[i]; name = attr.name; if (!filter || has(filter, name)) { if (getAttributeValue($target, attr) !== $oldstate[name]) { // The pushing is redundant but gzips very nicely mutations.push(MutationRecord({ type: "attributes", target: $target, attributeName: name, oldValue: $oldstate[name], attributeNamespace: attr.namespaceURI // in ie<8 it incorrectly will return undefined })); } checked[name] = true; } } for (name in $oldstate) { if (!checked[name]) { mutations.push(MutationRecord({ target: $target, type: "attributes", attributeName: name, oldValue: $oldstate[name] })); } } } /** * searchSubtree: array of mutations so far, element, element clone, bool * synchronous dfs comparision of two nodes * This function is applied to any observed element with childList or subtree specified * Sorry this is kind of confusing as shit, tried to comment it a bit... * codereview.stackexchange.com/questions/38351 discussion of an earlier version of this func * * @param {Array} mutations * @param {Node} $target * @param {!Object} $oldstate : A custom cloned node from clone() * @param {!Object} config : A custom mutation config */ function searchSubtree(mutations, $target, $oldstate, config) { // Track if the tree is dirty and has to be recomputed (#14). var dirty; /* * Helper to identify node rearrangment and stuff... * There is no gaurentee that the same node will be identified for both added and removed nodes * if the positions have been shuffled. * conflicts array will be emptied by end of operation */ function resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes) { // the distance between the first conflicting node and the last var distance = conflicts.length - 1; // prevents same conflict being resolved twice consider when two nodes switch places. // only one should be given a mutation event (note -~ is used as a math.ceil shorthand) var counter = -~((distance - numAddedNodes) / 2); var $cur; var oldstruct; var conflict; while (conflict = conflicts.pop()) { $cur = $kids[conflict.i]; oldstruct = $oldkids[conflict.j]; // attempt to determine if there was node rearrangement... won't gaurentee all matches // also handles case where added/removed nodes cause nodes to be identified as conflicts if (config.kids && counter && Math.abs(conflict.i - conflict.j) >= distance) { mutations.push(MutationRecord({ type: "childList", target: node, addedNodes: [$cur], removedNodes: [$cur], // haha don't rely on this please nextSibling: $cur.nextSibling, previousSibling: $cur.previousSibling })); counter--; // found conflict } // Alright we found the resorted nodes now check for other types of mutations if (config.attr && oldstruct.attr) findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter); if (config.charData && $cur.nodeType === 3 && $cur.nodeValue !== oldstruct.charData) { mutations.push(MutationRecord({ type: "characterData", target: $cur, oldValue: oldstruct.charData })); } // now look @ subtree if (config.descendents) findMutations($cur, oldstruct); } } /** * Main worker. Finds and adds mutations if there are any * @param {Node} node * @param {!Object} old : A cloned data structure using internal clone */ function findMutations(node, old) { var $kids = node.childNodes; var $oldkids = old.kids; var klen = $kids.length; // $oldkids will be undefined for text and comment nodes var olen = $oldkids ? $oldkids.length : 0; // if (!olen && !klen) return; // both empty; clearly no changes // we delay the intialization of these for marginal performance in the expected case (actually quite signficant on large subtrees when these would be otherwise unused) // map of checked element of ids to prevent registering the same conflict twice var map; // array of potential conflicts (ie nodes that may have been re arranged) var conflicts; var id; // element id from getElementId helper var idx; // index of a moved or inserted element var oldstruct; // current and old nodes var $cur; var $old; // track the number of added nodes so we can resolve conflicts more accurately var numAddedNodes = 0; // iterate over both old and current child nodes at the same time var i = 0, j = 0; // while there is still anything left in $kids or $oldkids (same as i < $kids.length || j < $oldkids.length;) while (i < klen || j < olen) { // current and old nodes at the indexs $cur = $kids[i]; oldstruct = $oldkids[j]; $old = oldstruct && oldstruct.node; if ($cur === $old) { // expected case - optimized for this case // check attributes as specified by config if (config.attr && oldstruct.attr) /* oldstruct.attr instead of textnode check */ findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter); // check character data if node is a comment or textNode and it's being observed if (config.charData && oldstruct.charData !== undefined$1 && $cur.nodeValue !== oldstruct.charData) { mutations.push(MutationRecord({ type: "characterData", target: $cur, oldValue: oldstruct.charData })); } // resolve conflicts; it will be undefined if there are no conflicts - otherwise an array if (conflicts) resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes); // recurse on next level of children. Avoids the recursive call when there are no children left to iterate if (config.descendents && ($cur.childNodes.length || oldstruct.kids && oldstruct.kids.length)) findMutations($cur, oldstruct); i++; j++; } else { // (uncommon case) lookahead until they are the same again or the end of children dirty = true; if (!map) { // delayed initalization (big perf benefit) map = {}; conflicts = []; } if ($cur) { // check id is in the location map otherwise do a indexOf search if (!map[id = getElementId($cur)]) { // to prevent double checking // mark id as found map[id] = true; // custom indexOf using comparitor checking oldkids[i].node === $cur if ((idx = indexOfCustomNode($oldkids, $cur, j)) === -1) { if (config.kids) { mutations.push(MutationRecord({ type: "childList", target: node, addedNodes: [$cur], // $cur is a new node nextSibling: $cur.nextSibling, previousSibling: $cur.previousSibling })); numAddedNodes++; } } else { conflicts.push({ // add conflict i: i, j: idx }); } } i++; } if ($old && // special case: the changes may have been resolved: i and j appear congurent so we can continue using the expected case $old !== $kids[i]) { if (!map[id = getElementId($old)]) { map[id] = true; if ((idx = indexOf($kids, $old, i)) === -1) { if (config.kids) { mutations.push(MutationRecord({ type: "childList", target: old.node, removedNodes: [$old], nextSibling: $oldkids[j + 1], // praise no indexoutofbounds exception previousSibling: $oldkids[j - 1] })); numAddedNodes--; } } else { conflicts.push({ i: idx, j: j }); } } j++; } } // end uncommon case } // end loop // resolve any remaining conflicts if (conflicts) resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes); } findMutations($target, $oldstate); return dirty; } /** * Utility * Cones a element into a custom data structure designed for comparision. https://gist.github.com/megawac/8201012 * * @param {Node} $target * @param {!Object} config : A custom mutation config * @return {!Object} : Cloned data structure */ function clone($target, config) { var recurse = true; // set true so childList we'll always check the first level return function copy($target) { var elestruct = { /** @type {Node} */ node: $target }; // Store current character data of target text or comment node if the config requests // those properties to be observed. if (config.charData && ($target.nodeType === 3 || $target.nodeType === 8)) { elestruct.charData = $target.nodeValue; } // its either a element, comment, doc frag or document node else { // Add attr only if subtree is specified or top level and avoid if // attributes is a document object (#13). if (config.attr && recurse && $target.nodeType === 1) { /** * clone live attribute list to an object structure {name: val} * @type {Object.} */ elestruct.attr = reduce($target.attributes, function (memo, attr) { if (!config.afilter || config.afilter[attr.name]) { memo[attr.name] = getAttributeValue($target, attr); } return memo; }, {}); } // whether we should iterate the children of $target node if (recurse && (config.kids || config.charData || config.attr && config.descendents)) { /** @type {Array.} : Array of custom clone */ elestruct.kids = map($target.childNodes, copy); } recurse = config.descendents; } return elestruct; }($target); } /** * indexOf an element in a collection of custom nodes * * @param {NodeList} set * @param {!Object} $node : A custom cloned node * @param {number} idx : index to start the loop * @return {number} */ function indexOfCustomNode(set, $node, idx) { return indexOf(set, $node, idx, JSCompiler_renameProperty("node")); } // using a non id (eg outerHTML or nodeValue) is extremely naive and will run into issues with nodes that may appear the same like
  • var counter = 1; // don't use 0 as id (falsy) /** @const */ var expando = "mo_id"; /** * Attempt to uniquely id an element for hashing. We could optimize this for legacy browsers but it hopefully wont be called enough to be a concern * * @param {Node} $ele * @return {(string|number)} */ function getElementId($ele) { try { return $ele.id || ($ele[expando] = $ele[expando] || counter++); } catch (o_O) { // ie <8 will throw if you set an unknown property on a text node try { return $ele.nodeValue; // naive } catch (shitie) { // when text node is removed: https://gist.github.com/megawac/8355978 :( return counter++; } } } /** * **map** Apply a mapping function to each item of a set * @param {Array|NodeList} set * @param {Function} iterator */ function map(set, iterator) { var results = []; for (var index = 0; index < set.length; index++) { results[index] = iterator(set[index], index, set); } return results; } /** * **Reduce** builds up a single result from a list of values * @param {Array|NodeList|NamedNodeMap} set * @param {Function} iterator * @param {*} [memo] Initial value of the memo. */ function reduce(set, iterator, memo) { for (var index = 0; index < set.length; index++) { memo = iterator(memo, set[index], index, set); } return memo; } /** * **indexOf** find index of item in collection. * @param {Array|NodeList} set * @param {Object} item * @param {number} idx * @param {string} [prop] Property on set item to compare to item */ function indexOf(set, item, idx, prop) { for (; idx < set.length; idx++) { // start idx is always given as this is internal if ((prop ? set[idx][prop] : set[idx]) === item) return idx; } return -1; } /** * @param {Object} obj * @param {(string|number)} prop * @return {boolean} */ function has(obj, prop) { return obj[prop] !== undefined$1; // will be nicely inlined by gcc } // GCC hack see https://stackoverflow.com/a/23202438/1517919 function JSCompiler_renameProperty(a) { return a; } return MutationObserver; }(void 0); } /** * map-polyfill - A Map polyfill written in TypeScript, unit tested using Jasmine and Karma. * * @author Brenden Palmer * @version v0.0.1-alpha.2 * @license MIT */ !function () { var t; !function (t) { var e = function () { function t(t, e) { this.index = 0, this.map = null, this.done = !1, this.map = t, this.type = e; } return t.prototype.next = function () { var t; return this.map.keyArray.length > this.index ? ("entries" === this.type ? t = [this.map.keyArray[this.index], this.map.get(this.map.keyArray[this.index])] : "keys" === this.type ? t = this.map.keyArray[this.index] : "values" === this.type && (t = this.map.get(this.map.keyArray[this.index])), this.index++) : this.done = !0, { value: t, done: this.done }; }, t; }(); t.MapIterator = e; }(t || (t = {})); var t; !function (t) { var e = function () { function t() {} return Object.defineProperty(t, "MAP_KEY_IDENTIFIER", { get: function get() { return "MAP_KEY_IDENTIFIER_OZAbzyeCu3_spF91dwX14"; }, enumerable: !0, configurable: !0 }), Object.defineProperty(t, "MAP_SET_THROWABLE_MESSAGE", { get: function get() { return "Invalid value used as map key"; }, enumerable: !0, configurable: !0 }), t; }(); t.MapConstants = e; }(t || (t = {})); var t; !function (t) { var e = function () { function t() { if (null !== t.instance) throw "Get the instance of the MapSequencer using the getInstance method."; this.identifier = 0; } return t.getInstance = function () { return null === t.instance && (t.instance = new t()), t.instance; }, t.prototype.next = function () { return "Map_CJPOYUrpwK_aHBtMHXsTM" + String(this.identifier++); }, t.instance = null, t; }(); t.MapSequencer = e; }(t || (t = {})); var t; !function (t) { var e = function () { function e() {} return e.defineProperty = function (n) { var r; if (e.isValidObject(n) === !1) throw new TypeError(t.MapConstants.MAP_SET_THROWABLE_MESSAGE); if ("undefined" == typeof n[t.MapConstants.MAP_KEY_IDENTIFIER]) { r = t.MapSequencer.getInstance().next(); try { Object.defineProperty(n, t.MapConstants.MAP_KEY_IDENTIFIER, { enumerable: !1, configurable: !1, get: function get() { return r; } }); } catch (i) { throw new TypeError(t.MapConstants.MAP_SET_THROWABLE_MESSAGE); } } else r = n[t.MapConstants.MAP_KEY_IDENTIFIER]; return r; }, e.getProperty = function (n) { return e.isValidObject(n) === !0 ? n[t.MapConstants.MAP_KEY_IDENTIFIER] : void 0; }, e.isValidObject = function (t) { return t === Object(t); }, e; }(); t.MapUtils = e; }(t || (t = {})); var t; !function (t) { var e = function () { function e(t) { void 0 === t && (t = []), this.map = {}, this.keyArray = []; for (var e = 0; e < t.length; e++) { var n = t[e]; n && n.length >= 2 && this.set(n[0], n[1]); } } return e.prototype.get = function (e) { if (this.has(e) === !0) { var n = t.MapUtils.getProperty(e); return void 0 === n && (n = String(e)), this.map[n]; } }, e.prototype.has = function (e) { var n = t.MapUtils.getProperty(e); return void 0 === n && (n = String(e)), void 0 !== n && "undefined" != typeof this.map[n]; }, e.prototype["delete"] = function (e) { if (this.has(e) === !0) { var n = t.MapUtils.getProperty(e); return void 0 === n && (n = String(e)), this.keyArray.splice(this.keyArray.indexOf(e), 1), delete this.map[n], !0; } return !1; }, e.prototype.set = function (e, n) { this["delete"](e); var r; try { r = String(t.MapUtils.defineProperty(e)); } catch (i) { r = String(e); } this.keyArray.push(e), this.map[r] = n; }, e.prototype.entries = function () { return new t.MapIterator(this, "entries"); }, e.prototype.keys = function () { return new t.MapIterator(this, "keys"); }, e.prototype.values = function () { return new t.MapIterator(this, "values"); }, e.prototype.forEach = function (t, e) { for (var n = 0, r = this.keyArray; n < r.length; n++) { var i = r[n]; e ? t.call(e, this.get(i), i, this) : t(this.get(i), i, this); } }, e.prototype.clear = function () { this.map = {}, this.keyArray = []; }, e; }(); t.Map = e; }(t || (t = {})); var t; !function (t) { window.Map || (window.Map = t.Map); }(t || (t = {})); }(); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function (obj) { return typeof obj; }; } else { _typeof = function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function () {}; return { s: F, n: function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function (e) { throw e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function () { it = it.call(o); }, n: function () { var step = it.next(); normalCompletion = step.done; return step; }, e: function (e) { didErr = true; err = e; }, f: function () { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } /** * @this {Promise} */ function finallyConstructor(callback) { var constructor = this.constructor; return this.then(function (value) { // @ts-ignore return constructor.resolve(callback()).then(function () { return value; }); }, function (reason) { // @ts-ignore return constructor.resolve(callback()).then(function () { // @ts-ignore return constructor.reject(reason); }); }); } function allSettled(arr) { var P = this; return new P(function (resolve, reject) { if (!(arr && typeof arr.length !== 'undefined')) { return reject(new TypeError(_typeof(arr) + ' ' + arr + ' is not iterable(cannot read property Symbol(Symbol.iterator))')); } var args = Array.prototype.slice.call(arr); if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { if (val && (_typeof(val) === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call(val, function (val) { res(i, val); }, function (e) { args[i] = { status: 'rejected', reason: e }; if (--remaining === 0) { resolve(args); } }); return; } } args[i] = { status: 'fulfilled', value: val }; if (--remaining === 0) { resolve(args); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); } /** * @constructor */ function AggregateError(errors, message) { this.name = 'AggregateError', this.errors = errors; this.message = message || ''; } AggregateError.prototype = Error.prototype; function any$1(arr) { var P = this; return new P(function (resolve, reject) { if (!(arr && typeof arr.length !== 'undefined')) { return reject(new TypeError('Promise.any accepts an array')); } var args = Array.prototype.slice.call(arr); if (args.length === 0) return reject(); var rejectionReasons = []; for (var i = 0; i < args.length; i++) { try { P.resolve(args[i]).then(resolve)["catch"](function (error) { rejectionReasons.push(error); if (rejectionReasons.length === args.length) { reject(new AggregateError(rejectionReasons, 'All promises were rejected')); } }); } catch (ex) { reject(ex); } } }); } // Store setTimeout reference so promise-polyfill will be unaffected by // other code modifying setTimeout (like sinon.useFakeTimers()) var setTimeoutFunc = setTimeout; function isArray$1(x) { return Boolean(x && typeof x.length !== 'undefined'); } function noop() {} // Polyfill for Function.prototype.bind function bind$5(fn, thisArg) { return function () { fn.apply(thisArg, arguments); }; } /** * @constructor * @param {Function} fn */ function Promise$1(fn) { if (!(this instanceof Promise$1)) throw new TypeError('Promises must be constructed via new'); if (typeof fn !== 'function') throw new TypeError('not a function'); /** @type {!number} */ this._state = 0; /** @type {!boolean} */ this._handled = false; /** @type {Promise|undefined} */ this._value = undefined; /** @type {!Array} */ this._deferreds = []; doResolve(fn, this); } function handle(self, deferred) { while (self._state === 3) { self = self._value; } if (self._state === 0) { self._deferreds.push(deferred); return; } self._handled = true; Promise$1._immediateFn(function () { var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { (self._state === 1 ? resolve : reject)(deferred.promise, self._value); return; } var ret; try { ret = cb(self._value); } catch (e) { reject(deferred.promise, e); return; } resolve(deferred.promise, ret); }); } function resolve(self, newValue) { try { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.'); if (newValue && (_typeof(newValue) === 'object' || typeof newValue === 'function')) { var then = newValue.then; if (newValue instanceof Promise$1) { self._state = 3; self._value = newValue; finale(self); return; } else if (typeof then === 'function') { doResolve(bind$5(then, newValue), self); return; } } self._state = 1; self._value = newValue; finale(self); } catch (e) { reject(self, e); } } function reject(self, newValue) { self._state = 2; self._value = newValue; finale(self); } function finale(self) { if (self._state === 2 && self._deferreds.length === 0) { Promise$1._immediateFn(function () { if (!self._handled) { Promise$1._unhandledRejectionFn(self._value); } }); } for (var i = 0, len = self._deferreds.length; i < len; i++) { handle(self, self._deferreds[i]); } self._deferreds = null; } /** * @constructor */ function Handler(onFulfilled, onRejected, promise) { this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.promise = promise; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, self) { var done = false; try { fn(function (value) { if (done) return; done = true; resolve(self, value); }, function (reason) { if (done) return; done = true; reject(self, reason); }); } catch (ex) { if (done) return; done = true; reject(self, ex); } } Promise$1.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); }; Promise$1.prototype.then = function (onFulfilled, onRejected) { // @ts-ignore var prom = new this.constructor(noop); handle(this, new Handler(onFulfilled, onRejected, prom)); return prom; }; Promise$1.prototype['finally'] = finallyConstructor; Promise$1.all = function (arr) { return new Promise$1(function (resolve, reject) { if (!isArray$1(arr)) { return reject(new TypeError('Promise.all accepts an array')); } var args = Array.prototype.slice.call(arr); if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { try { if (val && (_typeof(val) === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call(val, function (val) { res(i, val); }, reject); return; } } args[i] = val; if (--remaining === 0) { resolve(args); } } catch (ex) { reject(ex); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }; Promise$1.any = any$1; Promise$1.allSettled = allSettled; Promise$1.resolve = function (value) { if (value && _typeof(value) === 'object' && value.constructor === Promise$1) { return value; } return new Promise$1(function (resolve) { resolve(value); }); }; Promise$1.reject = function (value) { return new Promise$1(function (resolve, reject) { reject(value); }); }; Promise$1.race = function (arr) { return new Promise$1(function (resolve, reject) { if (!isArray$1(arr)) { return reject(new TypeError('Promise.race accepts an array')); } for (var i = 0, len = arr.length; i < len; i++) { Promise$1.resolve(arr[i]).then(resolve, reject); } }); }; // Use polyfill for setImmediate for performance gains Promise$1._immediateFn = // @ts-ignore typeof setImmediate === 'function' && function (fn) { // @ts-ignore setImmediate(fn); } || function (fn) { setTimeoutFunc(fn, 0); }; Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) { if (typeof console !== 'undefined' && console) { console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console } }; /** @suppress {undefinedVars} */ var globalNS = function () { // the only reliable means to get the global object is // `Function('return this')()` // However, this causes CSP violations in Chrome apps. if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } throw new Error('unable to locate global object'); }(); // Expose the polyfill if Promise is undefined or set to a // non-function value. The latter can be due to a named HTMLElement // being exposed by browsers for legacy reasons. // https://github.com/taylorhakes/promise-polyfill/issues/114 if (typeof globalNS['Promise'] !== 'function') { globalNS['Promise'] = Promise$1; } else { if (!globalNS.Promise.prototype['finally']) { globalNS.Promise.prototype['finally'] = finallyConstructor; } if (!globalNS.Promise.allSettled) { globalNS.Promise.allSettled = allSettled; } if (!globalNS.Promise.any) { globalNS.Promise.any = any$1; } } var object$2 = { author: 'Yumata', github: 'https://github.com/yumata/lampa-source', css_version: '3.3.4', app_version: '3.3.5', cub_site: 'cub.best', apk_link_download: 'https://github.com/lampa-app/LAMPA/releases/download/v1.12.3/app-lite-release.apk' }; var plugins$1 = []; Object.defineProperty(object$2, 'app_digital', { get: function get() { return parseInt(object$2.app_version.replace(/\./g, '')); } }); Object.defineProperty(object$2, 'css_digital', { get: function get() { return parseInt(object$2.css_version.replace(/\./g, '')); } }); /** * Ссылка на сайт CUB, которая зависит от региона пользователя */ Object.defineProperty(object$2, 'cub_site', { get: function get() { return window.vpn_region == 'ru' ? 'cub.black' : 'cub.best'; } }); /** * Список подключенных плагинов */ Object.defineProperty(object$2, 'plugins', { get: function get() { return plugins$1; }, set: function set(plugin) { if (_typeof(plugin) == 'object' && typeof plugin.type == 'string') { plugins$1.push(plugin); } } }); /** * Ссылка на GitHub с файлами приложения */ Object.defineProperty(object$2, 'github_lampa', { get: function get() { return window.lampa_settings.fix_widget ? 'http://lampa.mx/' : 'https://yumata.github.io/lampa/'; }, set: function set() {} }); /** * Старые зеркала, которые не используются больше, но могут быть полезны для обратной совместимости */ Object.defineProperty(object$2, 'old_mirrors', { get: function get() { return ['cub.red', 'cub.rip', 'standby.cub.red', 'kurwa-bober.ninja', 'nackhui.com']; }, set: function set() {} }); /** * Список актуальных зеркал */ Object.defineProperty(object$2, 'cub_mirrors', { get: function get() { var lampa = ['cub.best', 'cub.black', 'durex.monster', 'cubnotrip.top']; var users = localStorage.getItem('cub_mirrors') || '[]'; try { users = JSON.parse(users); } catch (e) { users = []; } if (Object.prototype.toString.call(users) === '[object Array]' && users.length) { return lampa.concat(users); } return lampa; }, set: function set() {} }); /** * Список зеркал для сокета, вынесены отдельно, так как могут отличаться от обычных зеркал */ Object.defineProperty(object$2, 'soc_mirrors', { get: function get() { return ['cub.best', 'kurwa-bober.ninja', 'nackhui.com']; }, set: function set() {} }); /** * Текущее доменное имя, которое используется для работы с CUB */ Object.defineProperty(object$2, 'cub_domain', { get: function get() { var use = localStorage.getItem('cub_domain') || ''; return object$2.cub_mirrors.indexOf(use) > -1 ? use : object$2.cub_mirrors[0]; } }); /** * Текущее живое зеркало с протоколом http|https */ Object.defineProperty(object$2, 'cub_alive', { get: function get() { return localStorage.getItem('cub_alive') || 'https://' + object$2.cub_domain; } }); /** * Ссылка на сайт CUB */ Object.defineProperty(object$2, 'qr_site', { get: function get() { return object$2.cub_domain + '/img/other/qr-code-strong.png'; } }); /** * Ссылка на QR для добавления устройства */ Object.defineProperty(object$2, 'qr_device_add', { get: function get() { return object$2.cub_domain + '/img/other/qr-add-device.png'; } }); /** * Подписка на события */ function Subscribe$1() { this.add = function (type, listener) { if (this._listeners === undefined) this._listeners = {}; var listeners = this._listeners; if (listeners[type] === undefined) listeners[type] = []; if (listeners[type].indexOf(listener) === -1) listeners[type].push(listener); return this; }; this.follow = function (type, listener) { var _this = this; type.split(',').forEach(function (name) { _this.add(name, listener); }); return this; }; this.has = function (type, listener) { if (this._listeners === undefined) return false; var listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; }; this.remove = function (type, listener) { if (this._listeners === undefined) return this; var listeners = this._listeners; var listenerArray = listeners[type]; if (listenerArray !== undefined) { var index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } return this; }; this.send = function (type) { var event = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (this._listeners === undefined) return this; try { var listeners = this._listeners; var listenerArray = listeners[type]; if (listenerArray !== undefined) { var array = listenerArray.slice(0); for (var i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } } } catch (e) { console.error('Subscribe', 'send error:', e.message, e.stack); } return this; }; this.destroy = function () { this._listeners = {}; }; } var Subscribe$2 = (function () { return new Subscribe$1(); }); function toObject(a) { if (Object.prototype.toString.call(a) === '[object Object]') return a;else { a = {}; return a; } } function toArray(a) { if (Object.prototype.toString.call(a) === '[object Object]') { var b = []; for (var i in a) { b.push(a[i]); } return b; } else if (typeof a == 'string' || a == null || typeof a == 'number' || typeof a == 'undefined') return [];else return a; } function decodeJson(string, empty) { var json = empty || {}; if (string) { try { json = JSON.parse(string); } catch (e) {} } return json; } function isObject(a) { return Object.prototype.toString.call(a) === '[object Object]'; } function isArray(a) { return Object.prototype.toString.call(a) === '[object Array]'; } function extend(a, b, replase) { for (var i in b) { if (isObject(b[i])) { if (a[i] == undefined) a[i] = {}; this.extend(a[i], b[i], replase); } else if (a[i] == undefined || replase) a[i] = b[i]; } } function empty$1(a, b) { for (var i in b) { if (!a[i]) a[i] = b[i]; } } function getKeys(a, add) { var k = add || []; for (var i in a) { k.push(i); } return k; } function getValues(a, add) { var k = add || []; for (var i in a) { k.push(a[i]); } return k; } function remove$7(from, need) { var inx = from.indexOf(need); if (inx >= 0) from.splice(inx, 1); } function clone$2(a) { return JSON.parse(JSON.stringify(a)); } function insert(where, index, item) { where.splice(index, 0, item); } function destroy$r(arr) { var call_function = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'destroy'; var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; var where = toArray(arr); for (var i = where.length - 1; i >= 0; i--) { if (where[i] && where[i][call_function]) where[i][call_function](value); } } function groupBy(xs, key) { return xs.reduce(function (rv, x) { (rv[x[key]] = rv[x[key]] || []).push(x); return rv; }, {}); } function removeNoIncludes(where, items) { for (var i = where.length - 1; i >= 0; i--) { if (items.indexOf(where[i]) === -1) remove$7(where, where[i]); } return where; } function shuffle(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } function shuffleArrayFromIndex(array, startIndex) { if (startIndex < 0 || startIndex >= array.length) { return; } for (var i = array.length - 1; i > startIndex; i--) { // Выбираем случайный индекс от startIndex до i var j = Math.floor(Math.random() * (i - startIndex + 1)) + startIndex; // Меняем местами элементы array[i] и array[j] var _ref = [array[j], array[i]]; array[i] = _ref[0]; array[j] = _ref[1]; } } function removeDuplicates(array, key) { var seen = []; return array.filter(function (a) { if (seen.indexOf(a[key]) == -1) { seen.push(a[key]); return true; } else return false; }); } function flatMap(arr, add) { var result = []; for (var i = 0; i < arr.length; i++) { result.push(arr[i]); if (i < arr.length - 1) { if (add && typeof add == 'function') { result.push(add(arr[i], arr[i + 1])); } else { result.push(add || null); } } } return result; } function unique(arr) { return arr.filter(function (item, pos) { return arr.indexOf(item) === pos; }); } var Arrays = { toObject: toObject, toArray: toArray, decodeJson: decodeJson, isObject: isObject, isArray: isArray, extend: extend, getKeys: getKeys, getValues: getValues, insert: insert, clone: clone$2, remove: remove$7, destroy: destroy$r, empty: empty$1, groupBy: groupBy, removeNoIncludes: removeNoIncludes, shuffle: shuffle, shuffleArrayFromIndex: shuffleArrayFromIndex, removeDuplicates: removeDuplicates, flatMap: flatMap, unique: unique }; var html$2t = "
    \n
    \n
    \n \n
    \n\n
    \n \n
    \n\n
    \n \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    "; var html$2s = "
    \n
    \n
    \n
    "; var html$2r = "
    \n\n
    \n
      \n \n
    \n
    \n\n
    \n\n
    \n
      \n
    • \n
      \n \n
      \n
      #{menu_settings}
      \n
    • \n\n
    • \n
      \n \n
      \n
      #{menu_about}
      \n
    • \n\n
    • \n
      \n \n
      \n
      #{menu_console}
      \n
    • \n
    \n
    \n\n
    \n\n
    \n
      \n
    • \n
      \n \n
      \n
      #{extensions_edit}
      \n
    • \n
    \n
    \n
    "; var html$2q = "
    \n
    \n
    "; var html$2p = "
    \n
    \n
    \n
    "; var html$2o = "
    \n
    \u041E\u0436\u0438\u0434\u0430\u044E
    \n
    \u041D\u0430\u0436\u043C\u0438\u0442\u0435 (\u0432\u043D\u0438\u0437) \u0434\u043B\u044F \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u0438.
    \n\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    "; var html$2n = "
    \n
    \n
    \n \n
    \n
    \n
    "; var html$2m = "
    \n
    \n
    \n
    \n
    #{title_settings}
    \n
    \n
    \n
    \n
    "; var html$2l = "
    \n
    \n
    \n \n \n \n \n \n \n
    \n
    #{settings_cub_sync}
    \n
    \n
    \n
    \n \n \n \n
    \n
    #{settings_main_interface}
    \n
    \n
    \n
    \n \n
    \n
    #{settings_main_player}
    \n
    \n
    \n
    \n \n
    \n
    #{settings_main_parser}
    \n
    \n
    \n
    \n \n
    \n
    #{settings_main_torrserver}
    \n
    \n
    \n
    \n \n \n \n
    \n
    TMDB
    \n
    \n
    \n
    \n \n \n \n \n \n
    \n
    #{settings_main_plugins}
    \n
    \n
    \n
    \n \n \n \n \n
    \n
    #{title_parental_control}
    \n
    \n
    \n
    \n \n
    \n
    #{settings_main_rest}
    \n
    \n
    "; var html$2k = "
    \n
    \n
    #{settings_interface_lang}
    \n
    \n
    \n\n
    \n
    #{settings_interface_type}
    \n
    \n
    \n\n
    \n
    #{settings_interface_size}
    \n
    \n
    \n\n
    \n
    #{settings_interface_menu_always}
    \n
    \n
    \n\n
    #{settings_interface_background}
    \n\n
    \n
    #{settings_interface_background_use}
    \n
    \n
    \n\n
    \n
    #{settings_interface_background_type}
    \n
    \n
    \n\n
    \n
    #{settings_interface_black_style}
    \n
    \n
    \n\n
    #{title_card}
    \n\n
    \n
    #{settings_interface_card_poster}
    \n
    \n
    \n\n
    \n
    #{settings_interface_card_cover}
    \n
    \n
    \n\n
    \n
    #{settings_interface_card_reactions}
    \n
    \n
    \n\n
    #{settings_interface_glass}
    \n\n
    \n
    #{settings_interface_glass}
    \n
    \n
    #{settings_interface_glass_descr}
    \n
    \n\n
    \n
    #{settings_interface_glass_opacity}
    \n
    \n
    \n\n
    \n
    #{settings_interface_sound}
    \n\n
    \n
    #{settings_interface_sound_play}
    \n
    \n
    \n\n
    \n
    #{settings_interface_sound_level}
    \n
    \n
    \n
    \n\n
    #{settings_interface_performance}
    \n\n\n
    \n
    #{settings_interface_animation}
    \n
    \n
    #{settings_interface_animation_descr}
    \n
    \n\n
    \n
    #{settings_interface_advanced_animation}
    \n
    \n
    \n\n
    \n
    #{settings_interface_attenuation}
    \n
    \n
    #{settings_interface_attenuation_descr}
    \n
    \n\n
    \n
    #{settings_interface_scroll}
    \n
    \n
    \n
    "; var html$2j = "
    \n
    \n
    #{settings_parser_use}
    \n
    \n
    #{settings_parser_use_descr}
    \n
    \n
    \n
    \n
    #{settings_parser_type}
    \n
    \n
    \n\n
    \n
    #{settings_server_link}
    \n
    \n
    \n\n
    \n
    #{settings_param_link_use_one}
    \n\n
    \n
    #{settings_parser_jackett_link}
    \n
    \n
    #{settings_parser_jackett_link_descr}
    \n
    \n\n
    \n
    #{settings_parser_jackett_key}
    \n
    \n
    #{settings_parser_jackett_key_descr}
    \n
    \n\n
    #{settings_param_link_use_two}
    \n\n
    \n
    #{settings_parser_jackett_link}
    \n
    \n
    #{settings_parser_jackett_link_descr}
    \n
    \n\n
    \n
    #{settings_parser_jackett_key}
    \n
    \n
    #{settings_parser_jackett_key_descr}
    \n
    \n\n
    \n
    #{settings_parser_jackett_interview}
    \n
    \n
    \n
    \n \n
    \n
    #{settings_param_link_use_one}
    \n\n
    \n
    #{settings_parser_jackett_link}
    \n
    \n
    #{settings_parser_prowlarr_link_descr}
    \n
    \n\n
    \n
    #{settings_parser_jackett_key}
    \n
    \n
    #{settings_parser_prowlarr_key_descr}
    \n
    \n\n
    #{settings_param_link_use_two}
    \n\n
    \n
    #{settings_parser_jackett_link}
    \n
    \n
    #{settings_parser_prowlarr_link_descr}
    \n
    \n\n
    \n
    #{settings_parser_jackett_key}
    \n
    \n
    #{settings_parser_prowlarr_key_descr}
    \n
    \n
    \n\n
    \n
    \n
    #{settings_server_search_type}
    \n
    \n
    #{settings_server_search_type_descr}
    \n
    \n
    \n\n
    #{more}
    \n\n
    \n
    #{settings_parser_search}
    \n
    \n
    #{settings_parser_search_descr}
    \n
    \n
    \n
    #{settings_parser_timeout_title}
    \n
    \n
    #{settings_parser_timeout_descr}
    \n
    \n
    \n
    #{settings_parser_in_search}
    \n
    \n
    #{settings_parser_in_search_descr}
    \n
    \n
    \n
    "; var html$2i = "
    \n
    \n
    #{settings_server_client}
    \n
    \n
    #{settings_server_client_descr}
    \n
    \n\n
    \n
    \n
    #{settings_server_link}
    \n
    \n
    \n\n
    #{settings_server_links}
    \n\n
    \n
    #{settings_server_link_one}
    \n
    \n
    #{settings_server_link_one_descr}
    \n
    \n
    \n\n
    \n
    #{settings_server_link_two}
    \n
    \n
    #{settings_server_link_two_descr}
    \n
    \n
    \n \n \n
    #{settings_server_additionally}
    \n\n
    \n
    #{settings_server_base}
    \n
    \n
    #{settings_server_base_descr}
    \n
    \n \n
    \n
    #{settings_server_preload}
    \n
    \n
    #{settings_server_preload_descr}
    \n
    \n\n
    \n
    #{settings_server_gts}
    \n
    \n
    #{settings_server_gts_descr}
    \n
    \n\n\t\t
    \n
    #{settings_server_tracktimecode}
    \n
    \n
    #{settings_server_tracktimecode_descr}
    \n
    \n\n
    #{settings_server_auth}
    \n\n
    \n
    #{settings_server_password_use}
    \n
    \n
    \n \n
    \n
    \n
    #{settings_server_login}
    \n
    \n
    \n\n
    \n
    #{settings_server_password}
    \n
    \n
    \n
    \n
    \n
    "; var html$2h = "
    \n
    \n
    #{settings_player_type}
    \n
    \n
    #{settings_player_type_descr}
    \n
    \n\n
    \n
    #{settings_player_iptv_type}
    \n
    \n
    #{settings_player_iptv_type_descr}
    \n
    \n\n
    \n
    #{settings_player_torrent_type}
    \n
    \n
    #{settings_player_torrent_type_descr}
    \n
    \n\n
    \n
    \n
    #{settings_infuse_launch_mode}
    \n
    \n
    #{settings_infuse_launch_mode_descr}
    \n
    \n
    \n\n
    \n
    #{settings_player_launch_trailers}
    \n
    \n
    \n \n
    \n
    #{settings_player_reset}
    \n
    \n
    #{settings_player_reset_descr}
    \n
    \n\n
    \n
    #{settings_player_path}
    \n
    \n
    #{settings_player_path_descr}
    \n
    \n \n
    \n
    #{settings_player_external_header}
    \n \n
    \n
    #{settings_player_external_fullscreen}
    \n
    \n
    \n
    \n\n
    \n
    #{settings_player_normalization}
    \n
    \n
    #{settings_player_normalization_descr}
    \n
    \n \n
    \n
    #{settings_player_next_episode}
    \n
    \n
    #{settings_player_next_episode_descr}
    \n
    \n\n
    \n
    #{settings_player_timecode}
    \n
    \n
    #{settings_player_timecode_descr}
    \n
    \n\n
    \n
    #{settings_player_scale}
    \n
    \n
    #{settings_player_scale_descr}
    \n
    \n\n
    \n
    #{settings_player_hls_title}
    \n
    \n
    #{settings_player_hls_descr}
    \n
    \n\n
    \n
    #{settings_player_rewind_title}
    \n
    \n
    #{settings_player_rewind_descr}
    \n
    \n \n
    \n
    #{settings_player_subs}
    \n\n
    \n
    #{settings_player_subs_use}
    \n
    \n
    #{settings_player_subs_use_descr}
    \n
    \n\n
    \n
    #{settings_player_subs_lang}
    \n
    \n
    #{settings_player_subs_lang_descr}
    \n
    \n
    \n\n
    #{more}
    \n\n
    \n
    #{settings_player_quality}
    \n
    \n
    #{settings_player_quality_descr}
    \n
    \n
    "; var html$2g = "
    \n
    \n
    #{settings_rest_start}
    \n
    \n
    #{settings_rest_start_descr}
    \n
    \n\n
    \n
    #{settings_rest_source_use}
    \n
    \n
    #{settings_rest_source_descr}
    \n
    \n\n
    \n
    #{settings_rest_protocol_use}
    \n
    \n
    #{settings_rest_protocol_descr}
    \n
    \n\n
    \n
    #{settings_request_caching_use}
    \n
    \n
    #{settings_request_caching_descr}
    \n
    \n\n
    #{settings_rest_screensaver}
    \n\n
    \n
    #{settings_rest_screensaver_use}
    \n
    \n
    \n\n
    \n
    #{settings_rest_screensaver_type}
    \n
    \n
    \n\n
    \n
    #{settings_rest_screensaver_time}
    \n
    \n
    \n\n \n
    #{more}
    \n\n
    \n
    #{settings_rest_pages}
    \n
    \n
    #{settings_rest_pages_descr}
    \n
    \n\n
    \n
    #{settings_rest_time}
    \n
    \n
    \n\n
    \n
    #{settings_rest_navigation}
    \n
    \n
    \n\n
    \n
    #{settings_rest_keyboard}
    \n
    \n
    \n\n\n
    \n
    #{settings_rest_card_quality}
    \n
    \n
    #{settings_rest_card_quality_descr}
    \n
    \n\n
    \n
    #{settings_rest_card_episodes}
    \n
    \n
    #{settings_rest_card_episodes_descr}
    \n
    \n\n
    \n
    #{settings_rest_device}
    \n
    \n
    \n
    "; var html$2f = "
    \n
    \n
    TMDB
    \n
    \n
    #{settings_rest_tmdb_lang}
    \n
    \n\n
    \n
    #{settings_rest_tmdb_posters}
    \n
    \n
    \n\n
    \n
    #{settings_rest_tmdb_prox_auto}
    \n
    \n
    \n\n
    \n
    #{settings_rest_tmdb_prox}
    \n
    \n
    \n\n
    \n
    Api
    \n
    \n
    #{settings_rest_tmdb_api_descr}
    \n
    \n\n
    \n
    Image
    \n
    \n
    #{settings_rest_tmdb_image_descr}
    \n
    \n
    "; var html$2e = "
    \n
    \n
    #{settings_plugins_add}
    \n
    #{settings_plugins_add_descr}
    \n
    \n
    \n
    #{settings_plugins_install}
    \n
    #{settings_plugins_install_descr}
    \n
    \n
    "; var html$2d = "
    \n
    \n
    \n
    https://{site}
    \n #{settings_cub_sync_descr}\n
    \n
    \n
    \n
    \n\n
    \n
    #{settings_cub_sync}
    \n
    \n
    \n\n
    #{settings_cub_account} Premium
    \n\n
    \n
    #{settings_cub_domain}
    \n
    \n
    \n\n
    \n
    #{settings_cub_logged_in_as}
    \n
    \n
    \n\n
    \n
    #{settings_cub_profile}
    \n
    \n
    \n\n
    \n
    #{settings_cub_logout}
    \n
    \n\n
    #{settings_cub_signin}
    \n\n
    \n
    #{settings_cub_signin_button}
    \n
    \n
    \n\n
    #{more}
    \n\n
    \n
    #{settings_cub_sync_btn}
    \n
    #{settings_cub_sync_btn_descr}
    \n
    \n\n
    \n
    #{settings_cub_backup}
    \n
    #{settings_cub_backup_descr}
    \n
    \n
    "; var html$2c = "
    \n
    \n
    {title}
    \n
    \n
    \n
    "; var html$2b = "
    \n
    \n \n\n
    \n
    \n \n
    \n
    \n
    \n\n
    {title}
    \n
    {release_year}
    \n
    "; var html$2a = "
    \n
    {Title}
    \n\n
    \n
    \n
    #{torrent_item_seeds}: {Seeders}
    \n
    #{torrent_item_grabs}: {Peers}
    \n
    \n
    {size}
    \n
    \n
    "; var html$29 = "
    \n
    \n
    \n
    \n
    "; var html$28 = "
    \n
    \n
    \n
    \n \n
    \n\n
    \n
    {num}
    \n
    {name}
    \n
    {date}
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    {title}
    \n
    {release_year}
    \n
    \n
    \n
    "; var html$27 = "
    \n\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    {title}
    \n
    {tagline}
    \n
    \n
    {rating}
    TMDB
    \n
    IMDB
    \n
    KP
    \n\n
    \n
    \n
    \n
    \n
    \n
    #{reactions_none}
    \n
    \n\n
    \n
    \n \n\n #{title_watch}\n
    \n\n
    \n \n \n \n\n #{settings_input_links}\n
    \n\n
    \n \n\n #{title_reactions}\n
    \n\n
    \n \n \n \n \n\n #{title_subscribe}\n
    \n\n
    \n \n
    \n
    \n
    \n
    \n\n
    \n
    \n \n\n #{full_torrents}\n
    \n\n
    \n \n\n #{full_trailers}\n
    \n
    \n
    "; var html$26 = "
    \n
    \n
    {text}
    \n\n
    \n
    \n
    #{full_date_of_release}
    \n
    {relise}
    \n
    \n\n
    \n
    #{full_budget}
    \n
    {budget}
    \n
    \n\n
    \n
    #{full_countries}
    \n
    {countries}
    \n
    \n
    \n\n
    \n
    \n\n \n
    "; var html$25 = "
    \n
    \n \n
    \n\n
    \n
    {name}
    \n
    {role}
    \n
    \n
    "; var html$24 = "
    \n
    {text}
    \n\n
    #{full_like}: {like_count}
    \n
    "; var html$23 = "
    \n
    \n \n
    \n\n
    \n
    {num}
    \n
    {name}
    \n
    {date}
    \n
    \n
    "; var html$22 = "
    \n \n
    "; var html$21 = "
    \n\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n \n \n \n
    \n
    001
    \n
    \n \n \n \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n \n
    #{player_playlist}
    \n
    \n
    \n \n
    #{torrent_error_next}
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    auto
    \n
    \n
    \n
    \n \n
    #{player_flow}
    \n
    \n
    \n \n
    #{player_subs}
    \n
    \n
    \n \n
    #{player_tracks}
    \n
    \n
    \n
    \n
    \n \n
    #{title_settings}
    \n
    \n
    \n \n
    #{player_pip}
    \n
    \n
    \n \n
    #{player_volume}
    \n
    \n \n
    \n
    \n
    \n \n
    #{player_size_cover_title}
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    "; var html$20 = "
    \n
    \n
    \n
    \n
    \n \n \n \n \n
    \n
    \n \u25C0\u25C0\u25C0\n
    \n
    \n \u25B6\u25B6\u25B6\n
    \n
    \n
    \n
    \n
    "; var html$1$ = "
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n
    \n
    \n #{loading}...\n
    \n
    \n
    \n \n
    \n
    \n \n
    \n
    \n
    \n\n
    \n
    \n
    "; var html$1_ = "
    \n \n
    "; var html$1Z = ""; var html$1Y = "
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    "; var html$1X = "
    \n
    {title}
    \n
    {subtitle}
    \n
    "; var html$1W = "
    \n
    {icon}
    \n
    \n
    {title}
    \n
    {subtitle}
    \n
    \n
    "; var html$1V = "
    \n
    \n
    \n
    \n
    \n \n \n \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    "; var html$1U = "
    \n
    \n \n
    \n
    \n
    \n #{filter_sorted}
    \n
    \n\n
    \n #{filter_filtred}
    \n
    \n
    "; var html$1T = "
    \n
    \n
    \n #{more}\n
    \n
    \n
    "; var html$1S = "
    \n
    #{search_input}...
    \n
    \n
    \n
    \n
    \n
    "; var html$1R = "
    \n
    \n
    \n\n
    \n\n
    #{settings_input_links}
    \n
    \n
    "; var html$1Q = "
    \n
    \n
    \n
    {title}
    \n
    \n
    \n \n
    \n
    \n
    "; var html$1P = "
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    {name}
    \n
    {place}
    \n
    \n
    "; var html$1O = "
    \n \n
    "; var html$1N = "
    \n
    \n
    {text}
    \n
    "; var html$1M = "
    \n
    \n \n
    \n
    {qr_text}
    \n
    \n\n
    \n
    {title}
    \n
    {text}
    \n
    \n
    "; var html$1L = "
    \n\n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    \n
    {birthday}
    \n
    \n
    \n \n
    {name}
    \n
    {place}
    \n\n
    \n
    \n \n \n \n
    \n\n
    \n \n \n \n \n \n
    \n
    \n
    \n
    \n
    "; var html$1K = "
    \n
    \n
    \n
    {title}
    \n
    {descr}
    \n
    "; var html$1J = "
    \n
    {title}
    \n
    {text}
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    "; var html$1I = "
    \n
    \n
    {title}
    \n
    {time}
    \n
    \n \n
    {descr}
    \n
    "; var html$1H = "
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    {title}
    \n
    {time}
    \n
    \n \n
    {descr}
    \n
    \n
    "; var html$1G = "
    \n
    {title}
    \n
    \n
    \n
    {date}
    \n
    {tracker}
    \n\n
    #{torrent_item_bitrate}: {bitrate} #{torrent_item_mb}
    \n
    #{torrent_item_seeds}: {seeds}
    \n
    #{torrent_item_grabs}: {grabs}
    \n \n
    {size}
    \n
    \n
    "; var html$1F = "
    \n
    {title}.{exe}
    \n
    {size}
    \n
    "; var html$1E = "
    \n
    \n
    \n \n
    \n\n
    \n
    {title}
    \n
    {original_title}
    \n
    \n
    \n
    \n \n
    \n
    "; var html$1D = "
    \n
    #{about_text}
    \n\n\n
    \n
    \n
    \n #{about_channel}
    \n @lampa_channel\n
    \n\n
    \n #{about_group}
    \n @lampa_group\n
    \n\n
    \n #{about_version}
    \n \n
    \n\n
    \n #{about_version} Android APK
    \n \n
    \n\n
    \n Hash
    \n 25ee3b0d1c845b4c71b87e7f3af98637\n
    \n\n
    \n Builded
    \n 2026-10-04 19:56\n
    \n
    \n
    \n\n
    \n

    #{termsofuse_t_01}

    \n\n

    #{termsofuse_t_02}

    \n\n
      \n
    1. \n
      #{termsofuse_t_03}
      \n\n
        \n
      1. #{termsofuse_t_04}

      2. \n\n
      3. #{termsofuse_t_05}

      4. \n\n
      5. #{termsofuse_t_06}

      6. \n\n
      7. #{termsofuse_t_07}

      8. \n
      \n \n
    2. \n\n
    3. \n
      #{termsofuse_t_08}
      \n\n
        \n
      1. #{termsofuse_t_09}

      2. \n
      3. #{termsofuse_t_10}

      4. \n
      \n
    4. \n\n
    5. \n
      #{termsofuse_t_11}
      \n\n
        \n
      1. #{termsofuse_t_12}

      2. \n
      3. #{termsofuse_t_13}

      4. \n
      \n
    6. \n
    \n
    \n
    "; var html$1C = "
    \n
    \n
    \n
    {title}
    \n
    {text}
    \n
    \n
    "; var html$1B = "
    \n
    \n \"\"\n
    \n
    \n
    \n
    \n
    {title}
    \n
    {time}
    \n
    \n\n
    \n\n
    \n
    {info}
    \n
    {quality}
    \n
    \n
    \n
    "; var html$1A = "
    \n \n {rate}\n
    "; var html$1z = "
    \n
    {name}
    \n
    {head}
    \n
    {overview}
    \n
    "; var html$1y = "
    \n
    \n
    \n
    {title}
    \n
    {text}
    \n
    \n
    \n\n
    \n
    \n
    \u041F\u0440\u0438\u0447\u0438\u043D\u044B
    \n
      \n
    • \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u0430\u0434\u0440\u0435\u0441: {ip}
    • \n
    • \u0422\u0435\u043A\u0443\u0449\u0438\u0439 \u0430\u0434\u0440\u0435\u0441 {ip} \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u043C!
    • \n
    • \u0422\u0435\u043A\u0443\u0449\u0438\u0439 \u043E\u0442\u0432\u0435\u0442: {echo}
    • \n
    \n
    \n\n
    \n
    \u041A\u0430\u043A \u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u043E?
    \n
      \n
    • \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u0430\u0434\u0440\u0435\u0441: 192.168.0.\u0445\u0445\u0445:8090
    • \n
    • \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u0432\u0435\u0440\u0441\u0438\u044E Matrix
    • \n
    \n
    \n\n
    \n
    \u041A\u0430\u043A \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C?
    \n
      \n
    • \u041D\u0430 \u044D\u0442\u043E\u043C \u0436\u0435 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0435, \u043E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438 \u0437\u0430\u0439\u0434\u0438\u0442\u0435 \u043F\u043E \u0430\u0434\u0440\u0435\u0441\u0443 {ip}/echo
    • \n
    • \u0415\u0441\u043B\u0438 \u0436\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u0442, \u043F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D \u043B\u0438 TorrServe, \u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0435\u0433\u043E.
    • \n
    • \u0415\u0441\u043B\u0438 \u0436\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043E\u0442\u0432\u0435\u0442\u0438\u043B, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044C \u0447\u0442\u043E \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u0435\u0441\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0430 MatriX
    • \n
    \n
    \n
    "; var html$1x = "
    \n
    \n
    \n
    {title}
    \n
    {text}
    \n
    \n
    \n\n
    \n
    \n
    \u041F\u0440\u0438\u0447\u0438\u043D\u044B
    \n
      \n
    • \u0417\u0430\u043F\u0440\u043E\u0441 \u043D\u0430 \u043F\u0438\u043D\u0433 \u0432\u0435\u0440\u043D\u0443\u043B \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442
    • \n
    • \u041E\u0442\u0432\u0435\u0442 \u043E\u0442 TorServer: {echo}
    • \n
    \n
    \n\n
    \n
    \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C?
    \n
      \n
    • \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044C \u0447\u0442\u043E \u0443 \u0432\u0430\u0441 \u0441\u0442\u043E\u0438\u0442 \u0432\u0435\u0440\u0441\u0438\u044F Matrix
    • \n
    \n
    \n\n
    \n
    \u041A\u0430\u043A \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C?
    \n
      \n
    • \u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438 \u0437\u0430\u0439\u0434\u0438\u0442\u0435 \u043F\u043E \u0430\u0434\u0440\u0435\u0441\u0443 {ip}/echo
    • \n
    • \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044C \u0447\u0442\u043E \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u0435\u0441\u0442\u044C \u043D\u0430\u043B\u0438\u0447\u0438\u0435 \u043A\u043E\u0434\u0430 MatriX
    • \n
    \n
    \n
    "; var html$1w = "
    \n
    \n
    \n
    {title}
    \n
    {text}
    \n
    \n
    \n\n
    \n
    \n
    #{torent_nohash_reasons}
    \n
      \n
    • #{torent_nohash_reason_one}
    • \n
    • #{torent_nohash_reason_two}: {echo}
    • \n
    • #{torent_nohash_reason_three}: {url}
    • \n
    \n
    \n\n
    \n
    #{torent_nohash_do}
    \n
      \n
    • #{torent_nohash_do_one}
    • \n
    • #{torent_nohash_do_two}
    • \n
    • #{torent_nohash_do_three}
    • \n
    \n
    \n\n
    \n
    #{torent_nohash_do}
    \n
      \n
    • #{torent_nohash_do_four}
    • \n
    • #{torent_nohash_do_five}
    • \n
    \n
    \n
    "; var html$1v = "
    \n
    \n \n
    \n
    \n
    #{torrent_install_need}
    \n
    #{torrent_install_text}
    \n \n
    #{torrent_install_contact}
    \n\n
    \n
    \n
    LG - Samsung
    \n
    @lampa_group
    \n
    \n
    \n
    \n
    "; var html$1u = "
    \n
    #{torrent_error_text}
    \n\n
    \n
    \n
    \n
    \n\n
    \n
    \n
      \n
    • #{torrent_error_step_1}
    • \n
    • #{torrent_error_step_2}
    • \n
    • #{torrent_error_step_3}
    • \n
    • #{torrent_error_step_4}
    • \n
    • #{torrent_error_step_5}
    • \n
    • #{torrent_error_step_6}
    • \n
    \n
    \n\n
    \n
    #{torrent_error_info_1}
    \n
    #{torrent_error_info_2}
    \n
    #{torrent_error_info_3}
    \n
    #{torrent_error_info_4}
    \n
    #{torrent_error_info_5}
    \n
    #{torrent_error_info_6}
    \n
    #{torrent_error_info_7}
    \n
    \n
    \n\n
    \n
    #{torrent_error_start}
    \n
    \n
    "; var html$1t = "
    \n \n
    \n
    \n
    {fname}
    \n
    #{torrent_serial_season} - {season}#{torrent_serial_date} - {air_date}
    \n
    \n
    \n
    {size}
    \n
    .{exe}
    \n
    \n
    \n
    \n
    {episode}
    \n
    "; var html$1s = "
    \n
    \n
    \n
    "; var html$1r = "
    \n
    \n
    \n
    "; var html$1q = "\n\n"; var html$1p = "\n\n"; var html$1o = "\n\n"; var html$1n = "\n\n"; var html$1m = "\n \n \n \n \n \n \n \n"; var html$1l = "\n \n \n \n"; var html$1k = "\n\n"; var html$1j = "\n\n"; var html$1i = "\n\n"; var html$1h = "\n\n"; var html$1g = "\n\n"; var html$1f = "\n\n"; var html$1e = "\n\n"; var html$1d = "\n\n\n\n\n"; var html$1c = "\n\n"; var html$1b = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; var html$1a = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; var html$19 = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; var html$18 = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; var html$17 = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; var html$16 = "\n\n\n\n\n"; var html$15 = "\n\n \n \n\n"; var html$14 = "
    \n
    \n
    "; var html$13 = "\n#{time_viewed} - {time} #{time_from} {duration} / {percent}\n"; var html$12 = "
    \n
    #{empty_title}
    \n
    #{empty_text}
    \n
    "; var html$11 = "
    \n
    \n \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    "; var html$10 = "
    \n\n
    \n
    #{plugins_catalog_work}
    \n
    #{plugins_catalog_work_descr}
    \n
    \n \n
    \n
    \n\n
    \n
    #{plugins_catalog_popular}
    \n
    #{plugins_catalog_popular_descr}
    \n
    \n \n
    \n
    \n
    "; var html$$ = "
    \n
    {text}
    \n\n
    \n\n
    \n \n
    \n
    "; var html$_ = "
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    "; var html$Z = "
    \n
    \n
    "; var html$Y = "
    \n
    \n
    {title}
    \n
    \n
    \n
    "; var html$X = "
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n \n
    \n
    \n
    \n
    #{player_disabled}
    \n
    \n
    "; var html$W = "
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    #{player_disabled}
    \n
    \n
    \n
    "; var html$V = "
    \n
    \n
    \n\n
    \n
    "; var html$U = "
    \n
    \n \n
    \n
    \n
    \n
    \n
    "; var html$T = "
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    "; var html$S = "
    \n
    \n \n
    \n
    "; var html$R = "
    \n
    \n \n \n \n \n
    \n\n
    \n #{account_create}\n
    \n
    "; var html$Q = "
    \n
    \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    \n\n
    \n #{account_limited}\n
    \n
    "; var html$P = "
    \n
    \n \n
    \n
    #{account_qr_create}
    \n
    \n\n
    \n
    #{account_none_title}
    \n
    #{account_create}
    \n
    - #{account_none_include_1}\n
    - #{account_none_include_2}\n
    - #{account_none_include_3}\n
    - #{account_none_include_4}\n
    \n\n
    #{settings_cub_signin_button}
    \n
    \n
    "; var html$O = "
    \n
    \n \n
    \n
    #{account_qr_premium}
    \n
    \n\n
    \n
    CUB Premium
    \n
    #{account_premium}
    \n
    - #{account_premium_include_1}\n
    - #{account_premium_include_2}\n
    - #{account_premium_include_4}\n
    \n
    \n
    "; var html$N = "
    \n
    CUB Premium
    \n
    \n #{account_premium}\n
    \n
    #{account_premium_more}
    \n
    {site}/premium
    \n
    "; var html$M = "
    \n
    \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n CUB Premium\n
    \n
    \n #{account_premium}\n
    \n
    \n
    \n
    #{account_premium_include_1}#{account_premium_include_text_1}
    \n
    #{filter_rating_from} 360 #{filter_rating_to} 5 040
    \n
    \n
    \n
    #{account_premium_include_2}#{account_premium_include_text_2}
    \n
    #{filter_rating_from} 100 #{filter_rating_to} 500
    \n
    \n
    \n
    #{account_premium_include_3}#{account_premium_include_text_3}
    \n
    #{filter_rating_from} 1 000 #{filter_rating_to} 10 000
    \n
    \n
    \n
    #{account_premium_include_4}#{account_premium_include_text_4}
    \n
    #{filter_rating_from} 3 #{filter_rating_to} 8
    \n
    \n
    \n
    #{account_premium_include_5}#{account_premium_include_text_5}
    \n
    \n
    \n
    \n
    #{account_premium_include_6}#{account_premium_include_text_6}
    \n
    \n
    \n
    \n
    #{account_premium_more}
    \n
    {site}/premium
    \n
    "; var html$L = "
    \n
    \n
    \n
    \n
    \n
    \n
    \n \"\"\n
    \n
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    "; var html$K = "
    \n \n \n \n \n \n \n
    "; var html$J = "
    \n
    \n \n \n \n \n \n \n \n \n \n
    \n
    \n #{https_text}\n
    \n
    "; var html$I = "
    \n
    \n
    \n
    \n \n
    \n
    #{back}
    \n
    \n\n
    \n
    \n \n
    \n
    #{title_main}
    \n
    \n\n
    \n
    \n \n
    \n
    #{search}
    \n
    \n\n
    \n
    \n \n
    \n
    #{menu_settings}
    \n
    \n
    \n
    "; var html$H = "
    \n
    \n \n
    \n
    {title}
    \n
    "; var html$G = "
    \n \n\n
    \n #{account_code_where}\n
    \n\n
    #{account_code_input}
    \n
    "; var html$F = "
    \n
    \n \n
    \n
    #{account_qr_code}
    \n
    \n\n
    \n
    #{account_add_device_title}
    \n
    #{account_add_device_text}
    \n\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n
    "; var html$E = "
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n\n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    "; var html$D = "
    \n
    \n \n
    \n\n
    \n
    \n
    \n
    \n
    "; var html$C = "
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n\n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n
    \n
    "; var html$B = "
    \n
    \n
    \n
    "; var html$A = "
    \n
    \n \n \n \n \n \n \n \n \n\n \n 0\n \n \n 5\n \n \n 10\n \n \n 15\n \n \n 20\n \n \n 30\n \n \n 60\n \n \n 100\n \n \n 200\n \n \n 500\n \n \n 0.000\n Mbps\n \n \n\n \n \n \n
    \n
    "; var html$z = "
    \n
    \n\n \n\n
    \n\n
    \n \n \n \n \n
    \n\n
    \n
    \n
    \n
    \n
    \n
    \n\n
    \n\n
    \n
    "; var html$y = "
    \n
    \n
    \n #{discuss_rules_title}\n
    \n\n
    \n
    #{discuss_rules_rule_1}
    \n
    #{discuss_rules_rule_2}
    \n
    #{discuss_rules_rule_3}
    \n
    #{discuss_rules_rule_4}
    \n
    #{discuss_rules_rule_5}
    \n
    #{discuss_rules_rule_6}
    \n
    #{discuss_rules_rule_7}
    \n
    \n
    \n
    "; var html$x = "
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    "; var html$w = "
    \n
    {icon}
    \n
    \n \n \n \n \n
    \n
    "; var html$v = "
    \n
    \n
    \n \n
    \n
    "; var html$u = "
    \n
    #{install_extension_rule_2}
    \n\n

    #{install_extension_rule_3}

    \n\n

    #{install_extension_rule_4}

    \n\n

    #{install_extension_rule_5}

    \n\n

    #{install_extension_rule_7}

    \n
    "; var html$t = "
    \n
    \n
    {text}
    \n
    \n
    \n \n \n \n \n \n \n \n \n \n \n
    \n
    "; var html$s = "
    \n
    \n \n
    \n
    \n \n
    \n
    "; var html$r = "\n\n \n \n \n\n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n\n \n \n \n \n \n\n \n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n\n \n \n \n \n\n \n \n \n \n\n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n\n \n \n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n"; var html$q = "
    \n
    {icon}
    \n
    \n
    {title}
    \n
    {text}
    \n
    \n
    \n
    "; var templates = { head: html$2t, wrap: html$2s, menu: html$2r, activitys: html$2q, activity: html$2p, activity_wait_refresh: html$2o, settings: html$2m, settings_main: html$2l, settings_interface: html$2k, settings_parser: html$2j, settings_server: html$2i, settings_player: html$2h, settings_more: html$2g, settings_tmdb: html$2f, settings_plugins: html$2e, settings_account: html$2d, scroll: html$2n, items_line: html$2c, card: html$2b, card_parser: html$2a, card_watched: html$29, card_episode: html$28, full_start_new: html$27, full_descr: html$26, full_person: html$25, full_review: html$24, full_episode: html$23, player: html$22, player_panel: html$21, player_video: html$20, player_info: html$1$, player_footer: html$1_, player_footer_card: html$1Z, selectbox: html$1Y, selectbox_item: html$1X, info: html$1V, more: html$1T, search: html$1S, settings_input: html$1R, modal: html$1Q, company: html$1P, modal_loading: html$1O, modal_pending: html$1N, modal_qr: html$1M, person_start: html$1L, empty: html$1K, empty_filter: html$1J, notice: html$1I, notice_card: html$1H, torrent: html$1G, torrent_file: html$1F, files: html$1E, about: html$1D, error: html$1C, torrent_noconnect: html$1y, torrent_file_serial: html$1t, torrent_nocheck: html$1x, torrent_nohash: html$1w, torrent_install: html$1v, torrent_error: html$1u, filter: html$1U, search_box: html$1s, console: html$1r, icon_star: html$1q, icon_viewed: html$1p, icon_lock: html$1o, icon_like: html$1n, icon_text: html$1m, icon_card: html$1l, icon_top: html$1k, icon_fire: html$1j, icon_hd: html$1i, icon_collection: html$1h, icon_search: html$1g, icon_settings: html$1f, icon_bell: html$1e, icon_bell_plus: html$1d, icon_broadcast: html$1c, icon_create_account: html$1b, icon_empty_subscribe: html$1a, icon_empty_bookmarks: html$19, icon_empty_history: html$18, icon_empty_torrents: html$17, icon_profile: html$16, icon_back: html$15, timeline: html$14, timeline_details: html$13, list_empty: html$12, screensaver: html$11, plugins_catalog: html$10, broadcast: html$$, lang_choice: html$_, extensions: html$Z, extensions_block: html$Y, extensions_item: html$X, extensions_recomend: html$W, extensions_info: html$V, extensions_theme: html$U, extensions_screensaver: html$T, iframe: html$S, account: html$R, account_limited: html$Q, account_none: html$P, account_premium: html$O, cub_premium: html$N, cub_premium_modal: html$M, selectbox_icon: html$1W, explorer: html$L, explorer_button_back: html$K, https: html$J, navigation_bar: html$I, head_backward: html$H, account_add_device: html$G, account_add_device_new: html$F, feed_item: html$E, feed_head: html$D, feed_episode: html$C, register: html$B, speedtest: html$A, season_episode: html$1B, season_episode_rate: html$1A, season_info: html$1z, ad_video_block: html$z, discuss_rules: html$y, bookmarks_folder: html$x, ai_search_animation: html$w, ai_agent: html$v, plugins_rules: html$u, remote_helper: html$t, watched_history: html$s, icons_sprite: html$r, warning: html$q }; var created = {}; /** * Получить шаблон * @param {string} name - имя шаблона * @param {object} [vars] - переменные для подстановки * @param {boolean} [like_static=false] - вернуть как строку, а не jQuery объект * @returns {jQuery|string} - jQuery объект или строка */ function get$g(name) { var vars = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var like_static = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var tpl = templates[name]; if (!tpl) throw 'Template [' + name + '] not found'; tpl = Lang.translate(tpl); for (var n in vars) { tpl = tpl.replace(new RegExp('{' + n + '}', 'g'), vars[n]); } tpl = tpl.replace(/{\@([a-z_-]+)}/g, function (e, s) { return templates[s] || ''; }); return like_static ? tpl : $(tpl); } /** * Получить шаблон как DOM элемент с возможностью подстановки переменных и вложенных DOM элементов * @param {string} name - имя шаблона * @param {object} [vars] - переменные для подстановки. Если значение - DOM элемент, он будет вставлен в шаблон * @returns {HTMLElement} - DOM элемент */ function js(name) { var vars = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!created[name]) { // создаём чистый шаблон без переменных var raw = get$g(name, {}, false)[0]; created[name] = raw.cloneNode(true); // кэшируем "чистый" DOM } // клонируем кэш var cloned = created[name].cloneNode(true); // подставим значения if (vars && Object.keys(vars).length > 0) { replaceVars(cloned, vars); } return cloned; } function getValueFromPath(obj, path) { return path.split('.').reduce(function (acc, key) { return acc && acc[key] != null ? acc[key] : ''; }, obj); } function replaceVars(root, vars) { var varRegex = /\{\{([a-zA-Z0-9_.]+)\}\}|\{([a-zA-Z0-9_]+)\}/g; function processNode(node) { // Если это текстовый узел и в нём есть подстановки if (node.nodeType === Node.TEXT_NODE) { var text = node.textContent; var matches = []; var m; // эквивалент [...text.matchAll(varRegex)] while ((m = varRegex.exec(text)) !== null) { matches.push(m); } if (matches.length > 0) { var parent = node.parentNode; // создаём новый документ-фрагмент для вставки var fragment = document.createDocumentFragment(); var lastIndex = 0; var _iterator = _createForOfIteratorHelper(matches), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var match = _step.value; var index = match.index; var raw = match[0]; var key = match[1] || match[2]; var value = getValueFromPath(vars, key); // Добавляем текст до переменной if (index > lastIndex) { fragment.appendChild(document.createTextNode(text.slice(lastIndex, index))); } // Вставляем значение if (value instanceof Node) { fragment.appendChild(value.cloneNode(true)); // вставляем DOM } else { fragment.appendChild(document.createTextNode(String(value))); } lastIndex = index + raw.length; } // Добавляем оставшийся текст } catch (err) { _iterator.e(err); } finally { _iterator.f(); } if (lastIndex < text.length) { fragment.appendChild(document.createTextNode(text.slice(lastIndex))); } // заменяем оригинальный текстовый узел parent.replaceChild(fragment, node); return; // больше не обрабатывать } } // Если есть атрибуты — заменим только строки // Не работает на старых браузерах, и не помню что оно делает // if (node.attributes) { // for (let attr of node.attributes) { // attr.value = attr.value.replace(varRegex, (_, nested, flat) => { // const key = nested || flat; // const value = getValueFromPath(vars, key); // return value instanceof Node ? '' : String(value); // }); // } // } // Рекурсивно обрабатываем детей for (var _i = 0, _Array$from = Array.from(node.childNodes); _i < _Array$from.length; _i++) { var child = _Array$from[_i]; processNode(child); } } processNode(root); } /** * Найти элементы по префиксу класса * @param {HTMLElement|jQuery} root - корневой элемент для поиска * @param {string} pref - префикс класса * @return {object} - объект с найденными элементами, ключи - части классов после префикса */ function prefix(root, pref) { var result = {}; // поддержка и для jQuery, и для обычного DOM var base = root instanceof jQuery ? root[0] : root; Array.from(base.querySelectorAll("[class*=\"".concat(pref, "__\"]"))).forEach(function (elem) { Array.from(elem.classList).forEach(function (cls) { if (cls.indexOf(pref + '__') === 0) { var key = cls.slice(pref.length + 2); // удалить pref__ result[key] = elem; } }); }); return result; } /** * Создать DOM элемент с возможностью добавления классов, атрибутов, текста, HTML и детей * @param {string} tag - имя тега * @param {object} [options] - опции для создания элемента * @param {string|string[]} [options.class] - класс или массив классов * @param {object} [options.attrs] - атрибуты в формате {имя: значение} * @param {string} [options.text] - текстовое содержимое * @param {string} [options.html] - HTML содержимое * @param {HTMLElement|jQuery|Array} [options.children] - дочерний элемент, jQuery объект или массив из них * @returns {HTMLElement} - созданный DOM элемент */ function elem$1(tag) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var element = document.createElement(tag); // Добавляем классы if (options["class"]) { element.addClass(options["class"]); } // Добавляем атрибуты if (options.attrs) { for (var _i2 = 0, _Object$entries = Object.entries(options.attrs); _i2 < _Object$entries.length; _i2++) { var _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2), key = _Object$entries$_i[0], value = _Object$entries$_i[1]; element.setAttribute(key, value); } } // Текст if (options.text) { element.textContent = options.text; } // HTML if (options.html) { element.innerHTML = options.html; } // Дети if (options.children) { var children = Array.isArray(options.children) ? options.children : [options.children]; var _iterator2 = _createForOfIteratorHelper(children), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var child = _step2.value; element.appendChild(child); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } return element; } /** * Добавить или обновить шаблон * @param {string} name - имя шаблона * @param {string} html - HTML шаблона * @returns {void} */ function add$h(name, html) { delete created[name]; templates[name] = html; } /** * Получить все шаблоны * @returns {object} - объект со всеми шаблонами */ function all$5() { return templates; } /** * Получить строковое представление шаблона * @param {string} name - имя шаблона * @returns {string} - HTML шаблона */ function string(name) { return templates[name] || ''; } var Template = { get: get$g, js: js, add: add$h, all: all$5, string: string, prefix: prefix, elem: elem$1 }; var Base64 = { // private property _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", // public method for encoding encode: function encode(input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = Base64._utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = (chr1 & 3) << 4 | chr2 >> 4; enc3 = (chr2 & 15) << 2 | chr3 >> 6; enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); } return output; }, // public method for decoding decode: function decode(input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = this._keyStr.indexOf(input.charAt(i++)); enc2 = this._keyStr.indexOf(input.charAt(i++)); enc3 = this._keyStr.indexOf(input.charAt(i++)); enc4 = this._keyStr.indexOf(input.charAt(i++)); chr1 = enc1 << 2 | enc2 >> 4; chr2 = (enc2 & 15) << 4 | enc3 >> 2; chr3 = (enc3 & 3) << 6 | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = Base64._utf8_decode(output); return output; }, // private method for UTF-8 encoding _utf8_encode: function _utf8_encode(string) { string = string.replace(/\r\n/g, "\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if (c > 127 && c < 2048) { utftext += String.fromCharCode(c >> 6 | 192); utftext += String.fromCharCode(c & 63 | 128); } else { utftext += String.fromCharCode(c >> 12 | 224); utftext += String.fromCharCode(c >> 6 & 63 | 128); utftext += String.fromCharCode(c & 63 | 128); } } return utftext; }, // private method for UTF-8 decoding _utf8_decode: function _utf8_decode(utftext) { var string = ""; var i = 0; var c = 0; var c2 = 0; var c3 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if (c > 191 && c < 224) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode((c & 31) << 6 | c2 & 63); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode((c & 15) << 12 | (c2 & 63) << 6 | c3 & 63); i += 3; } } return string; } }; var html$p = $('
    '), body$2 = html$p.find('.noty__text'), time$4; /** * Отображает уведомление * @param {string} text - текст уведомления * @param {Object} [params] - дополнительные параметры * @param {string} [params.style] - стиль уведомления, например 'error', 'success' и т.д. * @param {number} [params.time=3000] - время отображения уведомления в миллисекундах * @returns {void} */ function show$f(text) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; clearTimeout(time$4); html$p.attr('class', 'noty'); if (params.style) html$p.addClass('noty--style--' + params.style); time$4 = setTimeout(function () { html$p.removeClass('noty--visible'); }, params.time || 3000); body$2.html(text); html$p.addClass('noty--visible'); } function render$n() { return html$p; } var Noty = { show: show$f, render: render$n }; var widgetAPI, tvKey, pluginAPI, loader$2, orsay_loaded, orsay_call = Date.now(); function init$1q() { $('body').append($("
    \n \n \n \n
    ")); Utils$1.putScript(['$MANAGER_WIDGET/Common/API/Widget.js', '$MANAGER_WIDGET/Common/API/TVKeyValue.js', '$MANAGER_WIDGET/Common/API/Plugin.js', '$MANAGER_WIDGET/Common/webapi/1.0/webapis.js', '$MANAGER_WIDGET/Common/IME_XT9/ime.js', '$MANAGER_WIDGET/Common/IME_XT9/inputCommon/ime_input.js'], function () { try { if (typeof Common !== 'undefined' && Common.API && Common.API.TVKeyValue && Common.API.Plugin && Common.API.Widget) { widgetAPI = new Common.API.Widget(); tvKey = new Common.API.TVKeyValue(); pluginAPI = new Common.API.Plugin(); window.onShow = orsayOnshow; setTimeout(function () { orsayOnshow(); }, 2000); widgetAPI.sendReadyEvent(); } else { if (orsay_call + 5 * 1000 > Date.now()) setTimeout(orsayOnLoad, 50); } } catch (e) {} }); /** * Скрывает ненужные параметры для плеера */ if (Platform.is('orsay')) { var hidePlayerParams = function hidePlayerParams() { $('[data-name="player_normalization"],[data-name="player_scale_method"],[data-name="player_hls_method"]').toggleClass('hide', Lampa.Storage.field('player') == 'orsay'); }; /* Подписываемся на открытие настроек плера*/ Lampa.Settings.listener.follow('open', function (e) { if (e.name == 'player') hidePlayerParams(); }); /* Подписываемся на изменение плера*/ Lampa.Storage.listener.follow('change', function (e) { if (e.name == 'player') hidePlayerParams(); }); } } function orsayOnshow() { if (orsay_loaded) return; orsay_loaded = true; try { //Включает анимацию изменения громкости на ТВ и т.д. pluginAPI.SetBannerState(1); //Отключает перехват кнопок, этими кнопками управляет система ТВ pluginAPI.unregistKey(tvKey.KEY_INFO); pluginAPI.unregistKey(tvKey.KEY_TOOLS); pluginAPI.unregistKey(tvKey.KEY_MENU); pluginAPI.unregistKey(tvKey.KEY_VOL_UP); pluginAPI.unregistKey(tvKey.KEY_VOL_DOWN); pluginAPI.unregistKey(tvKey.KEY_MUTE); // Отключаем заставку ТВ pluginAPI.setOffScreenSaver(); console.log('App', 'Version widget ', Platform.version('orsay')); } catch (e) {} } function isNewWidget() { if (typeof window.top.WidgetLoader != 'undefined') { loader$2 = window.top.WidgetLoader; return true; } else { return false; } } function getLoaderUrl() { if (isNewWidget()) { return loader$2.getUrl(); } } function setLoaderUrl(_url) { if (isNewWidget()) { return loader$2.setUrl(_url); } } function changeLoaderUrl() { if (isNewWidget()) { return loader$2.changeUrl(); } } function exit$1() { if (widgetAPI) widgetAPI.sendReturnEvent(); } var Orsay = { init: init$1q, isNewWidget: isNewWidget, getLoaderUrl: getLoaderUrl, setLoaderUrl: setLoaderUrl, changeLoaderUrl: changeLoaderUrl, exit: exit$1 }; var sounds = {}; function Sound(option) { this.option = option; this.loaded = false; this.audio = new Audio(); this.audio.src = option.url; this.audio.volume = option.volume || 1; this.audio.load(); var volume = option.volume || 1; var isplay = false; this.audio.addEventListener("playing", function (event) { isplay = true; }); this.audio.addEventListener("pause", function (event) { isplay = false; }); this.play = function () { this.stop(); var playPromise; try { this.audio.currentTime = 0; this.audio.volume = volume * (Lampa.Storage.field('interface_sound_level') / 100); playPromise = this.audio.play(); } catch (e) {} if (playPromise !== undefined) { playPromise.then(function () {})["catch"](function (e) { console.log('Sound', 'play promise error:', e.message); }); } return this; }; this.stop = function () { if (!isplay) return this; var stopPromise; try { stopPromise = this.audio.pause(); } catch (e) {} if (stopPromise !== undefined) { stopPromise.then(function () {})["catch"](function (e) { console.log('Sound', 'stop promise error:', e.message); }); } return this; }; return this; } function init$1p() { if (Platform.is('android') || Platform.is('browser') || Platform.is('apple_tv') || Platform.desktop()) { add$g('hover', { url: object$2.github_lampa + 'sound/hover.ogg' }); add$g('enter', { url: object$2.github_lampa + 'sound/hover.ogg' }); add$g('bell', { url: object$2.github_lampa + 'sound/bell.ogg' }); } } function play$4(name) { if (sounds[name] && Storage.field('interface_sound_play')) sounds[name].play(); } function add$g(name, params) { try { sounds[name] = new Sound(params); return sounds[name]; } catch (e) { return false; } } var Sound$1 = { init: init$1p, add: add$g, play: play$4 }; var philipse = { play: typeof VK_PLAY !== 'undefined' ? VK_PLAY : typeof KEYCODE_MEDIA_PLAY !== 'undefined' ? KEYCODE_MEDIA_PLAY : -1, stop: typeof VK_STOP !== 'undefined' ? VK_STOP : typeof KEYCODE_MEDIA_STOP !== 'undefined' ? KEYCODE_MEDIA_STOP : -1, pause: typeof VK_PAUSE !== 'undefined' ? VK_PAUSE : typeof KEYCODE_MEDIA_PAUSE !== 'undefined' ? KEYCODE_MEDIA_PAUSE : -1, play_pause: typeof VK_PLAY_PAUSE !== 'undefined' ? VK_PLAY_PAUSE : typeof KEYCODE_MEDIA_PLAY_PAUSE !== 'undefined' ? KEYCODE_MEDIA_PLAY_PAUSE : -1 }; var enabled$2 = false; var listener$m = Subscribe$2(); var time$3 = 0; var lastdown = 0; var timer$9; var longpress; function toggle$d(new_status) { enabled$2 = new_status; listener$m.send('toggle', { status: enabled$2 }); } function enable$1() { toggle$d(true); } function disable() { toggle$d(false); } function isEnter(keycode) { return keycode == 13 || keycode == 29443 || keycode == 117 || keycode == 65385; } function keyCode(e) { var keycode; if (window.event) { keycode = e.keyCode; } else if (e.which) { keycode = e.which; } return keycode; } function keydownTrigger(e) { var keycode = keyCode(e); if (time$3 > Date.now() - 100) return; time$3 = Date.now(); listener$m.send('keydown', { code: keycode, enabled: enabled$2, event: e }); if (e.defaultPrevented) return; if (isEnter(keycode)) return; if (!enabled$2) return; //отключить все //4 - Samsung orsay if (keycode == 37 || keycode == 4) { Sound$1.play('hover'); listener$m.send('left', { code: keycode, enabled: enabled$2, event: e }); Controller.move('left'); } //29460 - Samsung orsay if (keycode == 38 || keycode == 29460) { Sound$1.play('hover'); listener$m.send('up', { code: keycode, enabled: enabled$2, event: e }); Controller.move('up'); } //5 - Samsung orsay if (keycode == 39 || keycode == 5) { Sound$1.play('hover'); listener$m.send('right', { code: keycode, enabled: enabled$2, event: e }); Controller.move('right'); } //5 - Samsung orsay //29461 - Samsung orsay if (keycode == 40 || keycode == 29461) { Sound$1.play('hover'); listener$m.send('down', { code: keycode, enabled: enabled$2, event: e }); Controller.move('down'); } //33 - LG; 427 - Samsung if (keycode == 33 || keycode == 427) { Sound$1.play('hover'); listener$m.send('toup', { code: keycode, enabled: enabled$2, event: e }); Controller.move('toup'); } //34 - LG; 428 - Samsung if (keycode == 34 || keycode == 428) { Sound$1.play('hover'); listener$m.send('todown', { code: keycode, enabled: enabled$2, event: e }); Controller.move('todown'); } //Абсолютный Enter //10252 - Samsung tizen if (keycode == 32 || keycode == 179 || keycode == 10252 || keycode == philipse.play_pause) { Controller.trigger('playpause'); } //Samsung media //71 - Samsung orsay if (keycode == 415 || keycode == 71 || keycode == philipse.play) { Controller.trigger('play'); } //Samsung stop //70 - Samsung orsay if (keycode == 413 || keycode == philipse.stop || keycode == 70) { Controller.trigger('stop'); } //69 - Samsung orsay if (keycode == 412 || keycode == 69 || keycode == 177) { Controller.trigger('rewindBack'); } //72 - Samsung orsay if (keycode == 418 || keycode == 417 || keycode == 72 || keycode == 176) { Controller.trigger('rewindForward'); } //74 - Samsung orsay if (keycode == 19 || keycode == 74 || keycode == philipse.pause) { Controller.trigger('pause'); } if (keycode == 457) { Controller.trigger('info'); } //E-Manual if (keycode == 10146) { e.preventDefault(); } if (keycode == 10133) { Controller.toggle('settings'); } //Кнопка назад //8 - браузер //27 //461 - LG //10009 - Samsung //88 - Samsung orsay if (keycode == 8 || keycode == 27 || keycode == 461 || keycode == 10009 || keycode == 88) { e.preventDefault(); listener$m.send('back', { code: keycode, enabled: enabled$2, event: e }); if (window.appready) Controller.back(); return false; } //Exit orsay if (keycode == 45) { Orsay.exit(); } //Кнопка pre-ch вызывает окно смены адреса в Loader //259 - Samsung orsay if (keycode == 259) { if (Orsay.isNewWidget()) { Orsay.changeLoaderUrl(); } } if (!(keycode == 122 || keycode == 123)) e.preventDefault(); } function init$1o() { window.addEventListener("keydown", function (e) { lastdown = keyCode(e); if (!timer$9) { timer$9 = setTimeout(function () { if (isEnter(lastdown)) { longpress = true; listener$m.send('longdown', { code: lastdown, enabled: enabled$2, event: e }); Controller["long"](); } }, 800); } }); window.addEventListener("keyup", function (e) { clearTimeout(timer$9); time$3 = 0; timer$9 = null; listener$m.send('keyup', { code: keyCode(e), enabled: enabled$2, event: e }); if (!longpress) { if (isEnter(keyCode(e)) && !e.defaultPrevented) { Sound$1.play('enter'); listener$m.send('enter', { code: keyCode(e), enabled: enabled$2, event: e }); Controller.enter(); } } else longpress = false; }); window.addEventListener("keydown", keydownTrigger); } var Keypad = { listener: listener$m, init: init$1o, enable: enable$1, disable: disable }; var timers$2 = []; var paused$1 = false; function init$1n() { setInterval(function () { var now = Date.now(); timers$2.forEach(function (t) { if (paused$1 && !t.background) return; if (now - t.last >= t.interval) { t.last = now; try { t.call(); } catch (e) { console.error('Timer', 'call error:', e); } } }); }, 1000); document.addEventListener('visibilitychange', function () { paused$1 = document.visibilityState !== 'visible'; console.log('Timer', 'visibility change:', document.visibilityState, 'paused:', paused$1); update$h(); }); // Разблокировка по клику или нажатию клавиши document.addEventListener('click', unpaused); Keypad.listener.follow('keydown', unpaused); } /** * Добавить таймер * @param {integer} interval - интервал в миллисекундах * @param {boolean} immediate - если true, то таймер будет вызван сразу при добавлении * @param {boolean} background - если true, то таймер будет работать в фоне, даже если вкладка не активна * @param {function} call - функция для вызова * @returns {void} */ function add$f(interval, call) { var immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var background = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; timers$2.push({ interval: interval, call: call, last: Date.now(), immediate: immediate, background: background }); } /** * Обновить таймеры * @returns {void} */ function update$h() { // Обновить метки времени, чтобы таймеры не "догоняли" timers$2.forEach(function (t) { return t.last = Date.now(); }); // Вызвать отложенные таймеры if (!paused$1) timers$2.forEach(function (t) { return t.immediate && t.call(); }); } /** * Разблокировать таймеры * @returns {void} */ function unpaused() { if (paused$1) { paused$1 = false; console.log('Timer', 'unpaused by action'); update$h(); } } /** * Удалить таймер * @param {function} call - функция для вызова * @returns {void} */ function remove$6(call) { timers$2 = timers$2.filter(function (t) { return t.call !== call; }); } var Timer = { init: init$1n, add: add$f, remove: remove$6, update: update$h }; var card_fields = ['poster_path', 'overview', 'release_date', 'genre_ids', 'id', 'original_title', 'original_language', 'title', 'backdrop_path', 'popularity', 'vote_count', 'vote_average', 'imdb_id', 'kinopoisk_id', 'original_name', 'name', 'first_air_date', 'origin_country', 'status', 'pg', 'release_quality', 'imdb_rating', 'kp_rating', 'source', 'number_of_seasons', 'number_of_episodes', 'next_episode_to_air', 'img', 'poster', 'background_image']; /** * Преобразование секунд в формат времени * @doc * @name secondsToTime * @alias Utils * @param {integer} sec время в секундах * @param {boolean} short короткое время * @returns {string} (hours : minutes : seconds) или (minutes : seconds) */ function secondsToTime(sec, _short) { var sec_num = parseInt(sec, 10); var hours = Math.floor(sec_num / 3600); var minutes = Math.floor((sec_num - hours * 3600) / 60); var seconds = sec_num - hours * 3600 - minutes * 60; if (hours < 10) { hours = "0" + hours; } if (minutes < 10) { minutes = "0" + minutes; } if (seconds < 10) { seconds = "0" + seconds; } if (isNaN(hours)) hours = '00'; if (isNaN(minutes)) minutes = '00'; if (isNaN(seconds)) seconds = '00'; if (_short) return hours + ':' + minutes; return hours + ':' + minutes + ':' + seconds; } /** * Преабразует первую букву строки в верхний регистр * @doc * @name capitalizeFirstLetter * @alias Utils * @param {string} string значение * @returns {string} */ function capitalizeFirstLetter(string) { string = string + ''; return string.charAt(0).toUpperCase() + string.slice(1); } /** * Сокращает строку до указанной длины * @doc * @name substr * @alias Utils * @param {string} txt текст * @param {integer} len длина * @returns {string} */ function substr(txt, len) { txt = txt || ''; return txt.length > len ? txt.substr(0, len) + '...' : txt; } function numberWithSpaces(x) { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " "); } function bytesToSize(bytes, speed) { if (bytes == 0) { return Lang.translate('size_zero'); } var unitMultiple = 1024; var unitNames = [Lang.translate('size_byte'), Lang.translate('size_kb'), Lang.translate('size_mb'), Lang.translate('size_gb'), Lang.translate('size_tb'), Lang.translate('size_pp')]; if (speed) { unitMultiple = 1000; unitNames = [Lang.translate('speed_bit'), Lang.translate('speed_kb'), Lang.translate('speed_mb'), Lang.translate('speed_gb'), Lang.translate('speed_tb'), Lang.translate('speed_pp')]; } var unitChanges = Math.floor(Math.log(bytes) / Math.log(unitMultiple)); return parseFloat((bytes / Math.pow(unitMultiple, unitChanges)).toFixed(2)) + ' ' + unitNames[unitChanges]; } function sizeToBytes(str) { var gsize = str.match(/([0-9\\.,]+)\s+(Mb|МБ|GB|ГБ|TB|ТБ)/i); if (gsize) { var size = parseFloat(gsize[1].replace(',', '.')); if (/gb|гб/.test(gsize[2].toLowerCase())) size *= 1024; if (/tb|тб/.test(gsize[2].toLowerCase())) size *= 1048576; return size * 1048576; } return 0; } function calcBitrate(byteSize, minutes) { if (!minutes) return 0; var sec = minutes * 60; var bitSize = byteSize * 8; return (bitSize / Math.pow(1000, 2) / sec).toFixed(2); } function getMoths(ended) { var need = ended ? '_e' : ''; var all = []; for (var i = 1; i <= 12; i++) { all.push(Lang.translate('month_' + i + need)); } return all; } function time$2(html) { var create = function create() { var _this = this; var where = html instanceof jQuery ? html[0] : html; var months = getMoths(); var months_end = getMoths(true); var days = [Lang.translate('day_7'), Lang.translate('day_1'), Lang.translate('day_2'), Lang.translate('day_3'), Lang.translate('day_4'), Lang.translate('day_5'), Lang.translate('day_6')]; var elem_clock = where.querySelector('.time--clock'); var elem_week = where.querySelector('.time--week'); var elem_day = where.querySelector('.time--day'); var elem_moth = where.querySelector('.time--moth'); var elem_full = where.querySelector('.time--full'); var listenTimeOffset = function listenTimeOffset(e) { if (e.name == 'time_offset') _this.tik(); }; Lampa.Storage.listener.follow('change', listenTimeOffset); this.tik = function () { var date = new Date(), time = date.getTime(), ofst = parseInt((localStorage.getItem('time_offset') == null ? 'n0' : localStorage.getItem('time_offset')).replace('n', '')); date = new Date(time + ofst * 1000 * 60 * 60); time = [date.getHours(), date.getMinutes(), date.getSeconds(), date.getFullYear()]; if (time[0] < 10) { time[0] = "0" + time[0]; } if (time[1] < 10) { time[1] = "0" + time[1]; } if (time[2] < 10) { time[2] = "0" + time[2]; } var current_time = [time[0], time[1]].join(':'), current_week = date.getDay(), current_day = date.getDate(); if (elem_clock) elem_clock.innerText = current_time; if (elem_week) elem_week.innerText = days[current_week]; if (elem_day) elem_day.innerText = current_day; if (elem_moth) elem_moth.innerText = months[date.getMonth()]; if (elem_full) elem_full.innerText = current_day + ' ' + months_end[date.getMonth()] + ' ' + time[3]; }; this.destroy = function () { Timer.remove(this.tik); Lampa.Storage.listener.remove('change', listenTimeOffset); }; Timer.add(60000, this.tik, true); this.tik(); }; return new create(); } function parseToDate(str) { if (typeof str == 'string') { str = str.toLowerCase().split('t')[0].replace(/-/g, '/'); } return new Date(str); } function parseTime(str) { var months = getMoths(); var months_end = getMoths(true); var days = [Lang.translate('day_7'), Lang.translate('day_1'), Lang.translate('day_2'), Lang.translate('day_3'), Lang.translate('day_4'), Lang.translate('day_5'), Lang.translate('day_6')]; var date = parseToDate(str); var time = [date.getHours(), date.getMinutes(), date.getSeconds(), date.getFullYear()]; if (time[0] < 10) { time[0] = "0" + time[0]; } if (time[1] < 10) { time[1] = "0" + time[1]; } if (time[2] < 10) { time[2] = "0" + time[2]; } var current_time = [time[0], time[1]].join(':'), current_week = date.getDay(), current_day = date.getDate(); return { time: current_time, time_full: [time[0], time[1], time[2]].join(':'), week: days[current_week], day: current_day, mouth: months[date.getMonth()], full: current_day + ' ' + months_end[date.getMonth()] + (new Date().getFullYear() == time[3] ? '' : ' ' + time[3]), "short": current_day + ' ' + months_end[date.getMonth()], briefly: current_day + ' ' + months_end[date.getMonth()] + ' ' + current_time }; } // function secondsToTimeHuman(sec_num) { // let hours = Math.trunc(sec_num / 3600) // let minutes = Math.floor((sec_num - hours * 3600) / 60) // return (hours ? hours + ' '+Lang.translate('time_h')+' ' : '') + (minutes ? minutes + ' '+Lang.translate('time_m')+' ' : Math.round(sec_num) + ' '+Lang.translate('time_s')) // } function secondsToTimeHuman(sec_num) { var hours = Math.trunc(sec_num / 3600); var minutes = Math.trunc(sec_num % 3600 / 60); // Остаток от деления используется для вычисления минут var seconds = Math.round(sec_num % 60); // Остаток от деления для секунд return (hours ? hours + ' ' + Lang.translate('time_h') + ' ' : '') + (minutes ? minutes + ' ' + Lang.translate('time_m') + ' ' : '') + (hours === 0 && minutes === 0 ? seconds + ' ' + Lang.translate('time_s') : ''); } function strToTime(str) { var date = new Date(str); return date.getTime(); } function checkHttp(url, http_only) { url = url + ''; if (url.indexOf("http://") == 0 || url.indexOf("https://") == 0) return url; url = (http_only ? 'http://' : protocol()) + url; return url; } function checkEmptyUrl(url) { url = url + ''; if (url.indexOf("http://") == 0 || url.indexOf("https://") == 0) return url; url = (window.location.protocol == 'https:' ? 'https://' : 'http://') + url; return url; } function rewriteIfHTTPS(u) { return window.location.protocol == 'https:' ? u.replace(/^(http:\/\/|https:\/\/)/, 'https://') : u; } function fixProtocolLink(u) { return rewriteIfHTTPS((localStorage.getItem('protocol') || 'https') + '://' + u.replace(/^(http:\/\/|https:\/\/)/, '')); } function fixMirrorLink(u) { object$2.old_mirrors.forEach(function (mirror) { u = u.replace('://' + mirror, '://' + object$2.cub_domain); }); return u; } function shortText(fullStr, strLen, separator) { fullStr = fullStr + ''; if (fullStr.length <= strLen) return fullStr; separator = separator || '...'; var sepLen = separator.length, charsToShow = strLen - sepLen, frontChars = Math.ceil(charsToShow / 2), backChars = Math.floor(charsToShow / 2); return fullStr.substr(0, frontChars) + separator + fullStr.substr(fullStr.length - backChars); } function protocol() { var prot = Storage.get('protocol') || 'https'; return window.location.protocol == 'https:' ? 'https://' : prot + '://'; } function addUrlComponent(url, params) { return url + (/\?/.test(url) ? '&' : '?') + params; } function putScript(items, complite, error, success, show_logs) { var p = 0; var l = typeof show_logs !== 'undefined' ? show_logs : true; function next() { if (p == items.length) return complite(); var u = items[p]; if (!u) { p++; return next(); } u = u.replace('cub.watch', Lampa.Manifest.cub_domain); if (l) console.log('Script', 'create:', u); var s = document.createElement('script'); s.onload = function () { if (l) console.log('Script', 'include:', u); if (success) success(u); next(); }; s.onerror = function () { if (l) console.warn('Script', 'error:', u); if (error) error(u); next(); }; s.async = true; s.setAttribute('src', u); document.body.appendChild(s); p++; } next(); } function putScriptAsync(items, complite, error, success, show_logs) { var p = 0; var l = typeof show_logs !== 'undefined' ? show_logs : true; function check() { p++; if (p == items.length && complite) complite(); } function put(u) { u = u.replace('cub.watch', Lampa.Manifest.cub_domain); if (l) console.log('Script', 'create:', u); var t = setTimeout(function () { s.onerror(); s.onload = null; s.onerror = null; }, 1000 * 60); var s = document.createElement('script'); s.onload = function () { if (l) console.log('Script', 'include:', u); clearTimeout(t); if (success) success(u); check(); }; s.onerror = function () { if (l) console.warn('Script', 'error:', u); clearTimeout(t); if (error) error(u); check(); }; s.async = true; s.setAttribute('src', u); document.body.appendChild(s); } for (var i = 0; i < items.length; i++) { put(items[i]); } } function putScriptOfMirrors(items, complite, error, success, show_logs) { var mirrors = Lampa.Manifest && Lampa.Manifest.cub_mirrors || []; var l = typeof show_logs !== 'undefined' ? show_logs : true; var p = 0; function nextItem() { if (p >= items.length) { if (complite) complite(); return; } var url = items[p++]; if (!url) { nextItem(); return; } var current = mirrors.find(function (m) { return url.includes(m); }); if (!current || !mirrors.length) { putScriptAsync([url], nextItem, function (u) { if (error) error(u); }, function (u) { if (success) success(u); }, show_logs); return; } var idx = mirrors.indexOf(current); var ordered = mirrors.slice(idx).concat(mirrors.slice(0, idx)); var i = 0; var loaded = false; function tryMirror() { if (i >= ordered.length) { if (l) console.warn('Script', 'all mirrors failed:', url); if (error) error(url); nextItem(); return; } var mirror = ordered[i++]; var tryUrl = url.replace(current, mirror); if (l) console.log('Script', 'mirror try [' + i + '/' + ordered.length + ']:', tryUrl); putScriptAsync([tryUrl], null, function () { if (!loaded) tryMirror(); }, function (u) { loaded = true; if (success) success(u); nextItem(); }, show_logs); } tryMirror(); } nextItem(); } function putStyle(items, complite, error) { var p = 0; function next() { if (p >= items.length) return complite(); var u = items[p]; $.get(u, function (css) { css = css.replace(/\.\.\//g, './'); var style = document.createElement('style'); style.type = 'text/css'; if (style.styleSheet) { // This is required for IE8 and below. style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } document.body.appendChild(style); next(); }, function () { if (error) error(u); next(); }, 'TEXT'); p++; } next(items[0]); } function clearTitle(title) { return title.replace(/[^a-zа-я0-9\s]/gi, ''); } function cardImgBackground(card_data) { if (Storage.field('background')) { if (Storage.field('background_type') == 'poster' && window.innerWidth > 790) { return card_data.backdrop_path ? Api.img(card_data.backdrop_path, 'w1280') : card_data.background_image ? card_data.background_image : ''; } return card_data.poster_path || card_data.profile_path ? Api.img(card_data.poster_path || card_data.profile_path) : card_data.poster || card_data.img || ''; } return ''; } function cardImgBackgroundBlur(card_data) { var uri = card_data.poster_path || card_data.profile_path ? Api.img(card_data.poster_path || card_data.profile_path, 'w200') : card_data.poster || card_data.img || ''; var pos = window.innerWidth > 400 && Storage.field('background_type') == 'poster' && !Storage.field('card_interfice_cover'); if (Storage.field('background')) { if (card_data.backdrop_path) uri = Api.img(card_data.backdrop_path, pos ? 'w1280' : 'w200');else if (card_data.background_image && pos) uri = card_data.background_image; } return uri; } function stringToHslColor(str, s, l) { var hash = 0; for (var i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); } var h = hash % 360; return 'hsl(' + h + ', ' + s + '%, ' + l + '%)'; } function pathToNormalTitle(path) { var add_exe = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var name = path.split('.'); var exe = name[name.length - 1]; name = name.join('.'); return (name + '').replace(/_|\./g, ' ') + (add_exe ? ' .' + exe + '' : ''); } function hash$2(input) { var str = (input || '') + ''; var hash = 0; if (str.length == 0) return hash; for (var i = 0; i < str.length; i++) { var _char = str.charCodeAt(i); hash = (hash << 5) - hash + _char; hash = hash & hash; // Convert to 32bit integer } return Math.abs(hash) + ''; } function uid(len) { var ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; var ID_LENGTH = len || 8; var id = ''; for (var i = 0; i < ID_LENGTH; i++) { id += ALPHABET.charAt(Math.floor(Math.random() * ALPHABET.length)); } return id; } function copyTextToClipboard(text, succes, error) { if (!text) return error && error(); var textArea = document.createElement("textarea"); textArea.value = text; textArea.style.top = "0"; textArea.style.left = "0"; textArea.style.position = "fixed"; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { var successful = document.execCommand('copy'); if (successful) succes();else error(); } catch (err) { error(); } document.body.removeChild(textArea); } function imgLoad(image, src, onload, onerror) { var img = image instanceof jQuery ? image[0] : image; img.onload = function () { onload && onload(img); }; img.onerror = function () { img.src = './img/img_broken.svg'; onerror && onerror(img); }; img.src = src; } function isTouchDevice() { if (!('ontouchstart' in window)) return false; var points = navigator.maxTouchPoints > 0 && navigator.maxTouchPoints !== 256 || navigator.msMaxTouchPoints > 0 && navigator.msMaxTouchPoints !== 256; var win = navigator.userAgent.toLowerCase().indexOf('windows nt') !== -1; return points && !win; } function canFullScreen() { var doc = window.document; var elem = doc.documentElement; return elem.requestFullscreen || elem.mozRequestFullScreen || elem.webkitRequestFullScreen || elem.msRequestFullscreen; } function toggleFullscreen() { // Check if the User-Agent string contains the word "Tesla" if (navigator.userAgent.indexOf("Tesla") >= 0) { // Get the current domain from the URL var currentDomain = window.location.hostname; // Construct the YouTube redirect URL with the current domain var targetURL = "https://www.youtube.com/redirect?q=".concat(currentDomain); // If it's Tesla's browser, redirect to the YouTube URL (which will open the current domain in full-screen, thank you Elon, sarcasm) location.href = targetURL; } else { var doc = window.document; var elem = doc.documentElement; var requestFullScreen = elem.requestFullscreen || elem.mozRequestFullScreen || elem.webkitRequestFullScreen || elem.msRequestFullscreen; var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { requestFullScreen.call(elem); } else { cancelFullScreen.call(doc); } } } function countSeasons(movie) { var seasons = movie.seasons || []; var count = 0; for (var i = 0; i < seasons.length; i++) { if (seasons[i].episode_count > 0) count++; } if (count > movie.number_of_seasons) count = movie.number_of_seasons; return count; } function countDays(time_a, time_b) { var d1 = new Date(time_a); var d2 = new Date(time_b); var days = (d2 - d1) / (1000 * 60 * 60 * 24); days = Math.round(days); return days <= 0 ? 0 : days; } function decodePG(pg) { var lang = Storage.field('language'); var keys = { 'G': '3+', 'PG': '6+', 'PG-13': '13+', 'R': '17+', 'NC-17': '18+', 'TV-Y': '0+', 'TV-Y7': '7+', 'TV-G': '3+', 'TV-PG': '6+', 'TV-14': '14+', 'TV-MA': '17+' }; if (lang == 'ru' || lang == 'uk' || lang == 'be') { for (var key in keys) { if (pg == key) return keys[key]; } } return pg; } /** * Проверяет можно ли показывать контент с указанным возрастным ограничением * @param {string|integer} pg возрастное ограничение (например, '16+', 'R', 18) * @param {integer} limit возраст профиля (по умолчанию 12) * @returns {boolean} */ function canWatchChildren(pg, limit) { var age = (pg + '').indexOf('+') >= 0 ? parseInt(pg.replace('+', '')) : 18; var age_limit = limit || 12; return Lampa.Account.Permit.child ? age_limit >= age : true; } function trigger$2(element, event_name) { var event = document.createEvent('Event'); event.initEvent(event_name, false, true); element.dispatchEvent(event); } function isPWA() { var pwa = false; try { pwa = window.matchMedia('(display-mode: standalone)').matches; } catch (e) {} return pwa; } function bigNumberToShort(number) { var suffixes = ['', 'K', 'M', 'M']; // Суффиксы для различных форматов var absoluteNumber = Math.abs(number); // Получаем абсолютное значение числа var suffixIndex = Math.floor((absoluteNumber.toFixed(0).length - 1) / 3); // Определение индекса суффикса // Проверяем, если число меньше 1000, возвращаем его без изменений if (absoluteNumber < 1000) { return number.toString(); } // Округление числа и преобразование в строку var roundedNumber = (number / Math.pow(1000, suffixIndex)).toFixed(1).replace('.0', ''); return roundedNumber + suffixes[suffixIndex]; // Возвращаем округленное число с суффиксом } function gup(name, url) { if (!url) url = location.href; name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); var regexS = "[\\?&]" + name + "=([^&#]*)"; var regex = new RegExp(regexS); var results = regex.exec(url); return results == null ? null : results[1]; } function dcma(media, id) { return window.lampa_settings.dcma && window.lampa_settings.dcma.find(function (a) { return a.cat == media && a.id == id; }); } function inputDisplay(value) { var f = value.trim(); var d = f.length - value.length; var e = d < 0 ? value.slice(d).replace(/\s/g, ' ') : ''; return f + e; } function filterCardsByType(items, need) { var filtred = []; var genres = function genres(card, id) { var gen = card.genres || card.genre_ids; if (gen && Object.prototype.toString.call(gen) === '[object Array]') { return gen.find(function (g) { if (_typeof(g) == 'object') return g.id == id; }); } return false; }; if (need == 'movies') filtred = items.filter(function (a) { return !a.name && !genres(a, 16); }); if (need == 'tv') filtred = items.filter(function (a) { return a.name && !genres(a, 16); }); if (need == 'multmovie') filtred = items.filter(function (a) { return !a.name && genres(a, 16); }); if (need == 'multtv') filtred = items.filter(function (a) { return a.name && genres(a, 16); }); return filtred; } function buildUrl$1(baseUrl, path, queryParams) { // Убираем /ts на конце, если он есть (для совместимости с TorrServer) var cleanUrl = baseUrl.replace(/\/ts\/?$/, ''); // Убираем лишние "/" в начале и конце пути, сохраняя subpath var url = cleanUrl.replace(/\/+$/, '') + '/' + path.replace(/^\/+/, ''); // Формируем строку запроса из массива объектов var queryString = queryParams.map(function (param) { return encodeURIComponent(param.name) + '=' + encodeURIComponent(param.value); }).join('&'); // Добавляем строку запроса к URL, если есть параметры return url + (queryString ? '?' + queryString : ''); } function simpleMarkdownParser(input) { // Удаление заголовков с маркером списка input = input.replace(/\* #{1,} /gim, '* '); // Жирный текст первым, чтобы ** не мешал распознаванию * как маркера списка input = input.replace(/\*\*(.*?)\*\*/gim, '$1'); // Маркер списка + заголовок: "* #### текст" или " * #### текст" input = input.replace(/^[ \t]*\*[ \t]*#{1,6}[ \t]+(.*$)/gim, '

    $1

    '); // Обычные заголовки input = input.replace(/^# (.*$)/gim, '

    $1

    '); input = input.replace(/^#{2,}[ \t]+(.*$)/gim, '

    $1

    '); // Маркированные списки с любым отступом и любым кол-вом пробелов после * input = input.replace(/^[ \t]*\*[ \t]+(.*$)/gim, '

    $1

    '); // Курсивный текст *текст* (после списков, чтобы одиночный * маркера не захватывался) input = input.replace(/\*(.*?)\*/gim, '$1'); // Оборачивание незатронутых строк в

    input = input.replace(/^(?!

    |

    |

    ||)(.+)$/gim, '

    $1

    '); // Удаление лишних переносов строк input = input.replace(/\n/gim, ''); return input; } function callWaiting(needCall, emergencyCall) { var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10000; var timer = setTimeout(emergencyCall, time); needCall(function () { clearTimeout(timer); }); } function clearCard(card) { var new_card = {}; var empty = ['original_name', 'name', 'first_air_date']; var num = ['popularity', 'vote_count', 'vote_average', 'imdb_rating', 'kp_rating', 'number_of_episodes', 'number_of_seasons']; card_fields.forEach(function (f) { if (typeof card[f] !== 'undefined') { var val = card[f]; if (val == null || val == 'NaN') val = ''; if (num.indexOf(f) >= 0 && !val) val = 0; new_card[f] = val; if (empty.indexOf(f) >= 0 && !val) delete new_card[f]; } }); if (new_card.poster_path) new_card.img = Lampa.Api.img(new_card.poster_path, 'w300'); return new_card; } function resolutionToQuality(width, height, symbol) { var quality = 0; if (width >= 3830) quality = 2160;else if (width >= 2550) quality = 1440;else if (width >= 1910) quality = 1080;else if (width >= 1014) quality = 720;else if (width >= 710) quality = 480;else if (width >= 630) quality = 360; if (symbol) quality += symbol; return quality; } function qualityToText(quality) { var text = ''; switch (quality) { case '2160p': text = '4K'; break; case '1440p': text = '2K'; break; case '1080p': text = 'FHD'; break; case '720p': text = 'HD'; break; case '480p': text = 'SD'; break; case '360p': text = 'SD'; break; default: text = quality; break; } return text; } function guid() { var hex = "0123456789ABCDEF"; var gi = ""; for (var i = 0; i < 36; i++) { if (i === 8 || i === 13 || i === 18 || i === 23) { gi += "-"; } else { var r = Math.floor(Math.random() * 16); // Устанавливаем версию и variant по UUIDv4 спецификации if (i === 14) r = 4; // версия 4 if (i === 19) r = r & 0x3 | 0x8; // variant gi += hex[r]; } } return gi; } function createInstance(BaseClass, element) { var add_params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var replace = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; Arrays.extend(element, { params: {} }); Arrays.extend(element.params, add_params, replace); var item = typeof element.params.createInstance == 'function' ? element.params.createInstance(element) : new BaseClass(element); if (!item) return console.error('createInstance function must return class', element); if (element.params.emit && _typeof(element.params.emit) == 'object' && typeof item.use == 'function') { item.use(element.params.emit); if (typeof element.params.emit.onInit == 'function') { element.params.emit.onInit(item); } } return item; } function extendParams(element) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; Arrays.extend(element, { params: {} }); Arrays.extend(element.params, params, replace); } function extendItemsParams(items) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; if (!items || !items.length) return; for (var i = 0; i < items.length; i++) { if (!items[i]) continue; extendParams(items[i], params, replace); } } function qrcode(text, element, error) { try { var qr = window.qrcode(0, 'H'); qr.addData(text, 'Byte'); qr.make(); if (element instanceof jQuery) element = element[0]; element.innerHTML = qr.createSvgTag({ cellSize: 8, margin: 10 }); } catch (e) { error && error(e); } } function onceInit(func) { var inited = false; return function () { if (inited) return; inited = true; return func.apply(void 0, arguments); }; } function containsJapanese(text) { return /[\u3040-\u30FF\u31F0-\u31FF\uFF66-\uFF9F\u4E00-\u9FFF]/.test(text); } function randomMinMax(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } /** * Добавляет источник к элементам данных * @doc * @name addSource * @alias Utils * @param {object|array} data данные или массив данных * @param {string} source источник */ function addSource$1(data, source) { if (Arrays.isObject(data) && Arrays.isArray(data.results)) { data.results.forEach(function (item) { if (!item.source) item.source = source; }); } else if (Arrays.isArray(data)) { data.forEach(function (item) { if (!item.source) item.source = source; }); } return data; } function clearHtmlTags(str) { return str.replace(/<\/?[^>]+(>|$)/g, ""); } /** * Разбивает эпизоды на сезоны на основе даты выхода (air_date) * gap — минимальный разрыв между эпизодами (в днях), чтобы считать новым сезоном */ function splitEpisodesIntoSeasons(episodes) { var gap = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 90; if (!Array.isArray(episodes) || episodes.length === 0) return {}; // --- Вспомогательная функция: дата → число дней --- function dateToDays(date) { if (!date || typeof date !== 'string') return 0; var parts = date.split("-"); if (parts.length !== 3) return 0; return parseInt(parts[0], 10) * 365 + parseInt(parts[1], 10) * 30 + parseInt(parts[2], 10); } // Сортируем по исходному номеру episodes = _toConsumableArray(episodes).sort(function (a, b) { return a.episode_number - b.episode_number; }); var seasons = {}; // { seasonNumber: [episodes] } var seasonNum = 1; // текущий сезон var lastDay = 0; // день последнего эпизода var _iterator = _createForOfIteratorHelper(episodes), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var ep = _step.value; var day = dateToDays(ep.air_date); // Проверяем разрыв > gap → новый сезон if (day > 0 && lastDay > 0 && day - lastDay > gap) { seasonNum++; } if (day > 0) lastDay = day; if (!seasons[seasonNum]) seasons[seasonNum] = []; // Копируем эпизод и перенумеровываем var newEp = _objectSpread2({}, ep); newEp.season_number = seasonNum; newEp.episode_number = seasons[seasonNum].length + 1; newEp.id = 900000 + seasonNum * 1000 + newEp.episode_number; seasons[seasonNum].push(newEp); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return seasons; } var Utils$1 = { secondsToTime: secondsToTime, secondsToTimeHuman: secondsToTimeHuman, capitalizeFirstLetter: capitalizeFirstLetter, substr: substr, numberWithSpaces: numberWithSpaces, time: time$2, bytesToSize: bytesToSize, calcBitrate: calcBitrate, parseTime: parseTime, parseToDate: parseToDate, checkHttp: checkHttp, shortText: shortText, protocol: protocol, addUrlComponent: addUrlComponent, sizeToBytes: sizeToBytes, putScript: putScript, putScriptAsync: putScriptAsync, putScriptOfMirrors: putScriptOfMirrors, putStyle: putStyle, clearTitle: clearTitle, cardImgBackground: cardImgBackground, cardImgBackgroundBlur: cardImgBackgroundBlur, strToTime: strToTime, stringToHslColor: stringToHslColor, pathToNormalTitle: pathToNormalTitle, hash: hash$2, uid: uid, guid: guid, copyTextToClipboard: copyTextToClipboard, imgLoad: imgLoad, isTouchDevice: isTouchDevice, toggleFullscreen: toggleFullscreen, canFullScreen: canFullScreen, countSeasons: countSeasons, countDays: countDays, decodePG: decodePG, trigger: trigger$2, isPWA: isPWA, bigNumberToShort: bigNumberToShort, rewriteIfHTTPS: rewriteIfHTTPS, checkEmptyUrl: checkEmptyUrl, gup: gup, dcma: dcma, inputDisplay: inputDisplay, filterCardsByType: filterCardsByType, buildUrl: buildUrl$1, simpleMarkdownParser: simpleMarkdownParser, fixProtocolLink: fixProtocolLink, fixMirrorLink: fixMirrorLink, callWaiting: callWaiting, clearCard: clearCard, qualityToText: qualityToText, resolutionToQuality: resolutionToQuality, createInstance: createInstance, extendParams: extendParams, extendItemsParams: extendItemsParams, qrcode: qrcode, onceInit: onceInit, containsJapanese: containsJapanese, randomMinMax: randomMinMax, addSource: addSource$1, clearHtmlTags: clearHtmlTags, splitEpisodesIntoSeasons: splitEpisodesIntoSeasons, canWatchChildren: canWatchChildren }; var WorkerClass = null; try { var WorkerThreads = typeof module !== 'undefined' && typeof module.require === 'function' && module.require('worker_threads') || typeof __non_webpack_require__ === 'function' && __non_webpack_require__('worker_threads') || typeof require === 'function' && require('worker_threads'); WorkerClass = WorkerThreads.Worker; } catch(e) {} // eslint-disable-line function decodeBase64$1(base64, enableUnicode) { return Buffer.from(base64, 'base64').toString(enableUnicode ? 'utf16' : 'utf8'); } function createBase64WorkerFactory$2(base64, sourcemapArg, enableUnicodeArg) { var sourcemap = sourcemapArg === undefined ? null : sourcemapArg; var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg; var source = decodeBase64$1(base64, enableUnicode); var start = source.indexOf('\n', 10) + 1; var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : ''); return function WorkerFactory(options) { return new WorkerClass(body, Object.assign({}, options, { eval: true })); }; } function decodeBase64(base64, enableUnicode) { var binaryString = atob(base64); if (enableUnicode) { var binaryView = new Uint8Array(binaryString.length); for (var i = 0, n = binaryString.length; i < n; ++i) { binaryView[i] = binaryString.charCodeAt(i); } return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer)); } return binaryString; } function createURL(base64, sourcemapArg, enableUnicodeArg) { var sourcemap = sourcemapArg === undefined ? null : sourcemapArg; var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg; var source = decodeBase64(base64, enableUnicode); var start = source.indexOf('\n', 10) + 1; var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : ''); var blob = new Blob([body], { type: 'application/javascript' }); return URL.createObjectURL(blob); } function createBase64WorkerFactory$1(base64, sourcemapArg, enableUnicodeArg) { var url; return function WorkerFactory(options) { url = url || createURL(base64, sourcemapArg, enableUnicodeArg); return new Worker(url, options); }; } var kIsNodeJS = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'; function isNodeJS() { return false; } function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) { if (isNodeJS()) { return createBase64WorkerFactory$2(base64, sourcemapArg, enableUnicodeArg); } return createBase64WorkerFactory$1(base64, sourcemapArg, enableUnicodeArg); } var WorkerFactory$2 = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgLyogZXNsaW50LWRpc2FibGUgbm8tYml0d2lzZSAtLSB1c2VkIGZvciBjYWxjdWxhdGlvbnMgKi8KCiAgLyogZXNsaW50LWRpc2FibGUgdW5pY29ybi9wcmVmZXItcXVlcnktc2VsZWN0b3IgLS0gYWltaW5nIGF0DQogICAgYmFja3dhcmQtY29tcGF0aWJpbGl0eSAqLwoKICAvKioNCiAgKiBTdGFja0JsdXIgLSBhIGZhc3QgYWxtb3N0IEdhdXNzaWFuIEJsdXIgRm9yIENhbnZhcw0KICAqDQogICogSW4gY2FzZSB5b3UgZmluZCB0aGlzIGNsYXNzIHVzZWZ1bCAtIGVzcGVjaWFsbHkgaW4gY29tbWVyY2lhbCBwcm9qZWN0cyAtDQogICogSSBhbSBub3QgdG90YWxseSB1bmhhcHB5IGZvciBhIHNtYWxsIGRvbmF0aW9uIHRvIG15IFBheVBhbCBhY2NvdW50DQogICogbWFyaW9AcXVhc2ltb25kby5kZQ0KICAqDQogICogT3Igc3VwcG9ydCBtZSBvbiBmbGF0dHI6DQogICoge0BsaW5rIGh0dHBzOi8vZmxhdHRyLmNvbS90aGluZy83Mjc5MS9TdGFja0JsdXItYS1mYXN0LWFsbW9zdC1HYXVzc2lhbi1CbHVyLUVmZmVjdC1mb3ItQ2FudmFzSmF2YXNjcmlwdH0uDQogICoNCiAgKiBAbW9kdWxlIFN0YWNrQmx1cg0KICAqIEBhdXRob3IgTWFyaW8gS2xpbmdlbWFubg0KICAqIENvbnRhY3Q6IG1hcmlvQHF1YXNpbW9uZG8uY29tDQogICogV2Vic2l0ZToge0BsaW5rIGh0dHA6Ly93d3cucXVhc2ltb25kby5jb20vU3RhY2tCbHVyRm9yQ2FudmFzL1N0YWNrQmx1ckRlbW8uaHRtbH0NCiAgKiBUd2l0dGVyOiBAcXVhc2ltb25kbw0KICAqDQogICogQGNvcHlyaWdodCAoYykgMjAxMCBNYXJpbyBLbGluZ2VtYW5uDQogICoNCiAgKiBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbg0KICAqIG9idGFpbmluZyBhIGNvcHkgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uDQogICogZmlsZXMgKHRoZSAiU29mdHdhcmUiKSwgdG8gZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dA0KICAqIHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHMgdG8gdXNlLA0KICAqIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsDQogICogY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlDQogICogU29mdHdhcmUgaXMgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcNCiAgKiBjb25kaXRpb25zOg0KICAqDQogICogVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUNCiAgKiBpbmNsdWRlZCBpbiBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS4NCiAgKg0KICAqIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCAiQVMgSVMiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELA0KICAqIEVYUFJFU1MgT1IgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUw0KICAqIE9GIE1FUkNIQU5UQUJJTElUWSwgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EDQogICogTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUiBDT1BZUklHSFQNCiAgKiBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSIExJQUJJTElUWSwNCiAgKiBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcNCiAgKiBGUk9NLCBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SDQogICogT1RIRVIgREVBTElOR1MgSU4gVEhFIFNPRlRXQVJFLg0KICAqLwogIHZhciBCbHVyU3RhY2sgPSBmdW5jdGlvbiBCbHVyU3RhY2soKSB7CiAgICB0aGlzLnIgPSAwOwogICAgdGhpcy5nID0gMDsKICAgIHRoaXMuYiA9IDA7CiAgICB0aGlzLmEgPSAwOwogICAgdGhpcy5uZXh0ID0gbnVsbDsKICB9OwoKICB2YXIgbXVsVGFibGUgPSBbNTEyLCA1MTIsIDQ1NiwgNTEyLCAzMjgsIDQ1NiwgMzM1LCA1MTIsIDQwNSwgMzI4LCAyNzEsIDQ1NiwgMzg4LCAzMzUsIDI5MiwgNTEyLCA0NTQsIDQwNSwgMzY0LCAzMjgsIDI5OCwgMjcxLCA0OTYsIDQ1NiwgNDIwLCAzODgsIDM2MCwgMzM1LCAzMTIsIDI5MiwgMjczLCA1MTIsIDQ4MiwgNDU0LCA0MjgsIDQwNSwgMzgzLCAzNjQsIDM0NSwgMzI4LCAzMTIsIDI5OCwgMjg0LCAyNzEsIDI1OSwgNDk2LCA0NzUsIDQ1NiwgNDM3LCA0MjAsIDQwNCwgMzg4LCAzNzQsIDM2MCwgMzQ3LCAzMzUsIDMyMywgMzEyLCAzMDIsIDI5MiwgMjgyLCAyNzMsIDI2NSwgNTEyLCA0OTcsIDQ4MiwgNDY4LCA0NTQsIDQ0MSwgNDI4LCA0MTcsIDQwNSwgMzk0LCAzODMsIDM3MywgMzY0LCAzNTQsIDM0NSwgMzM3LCAzMjgsIDMyMCwgMzEyLCAzMDUsIDI5OCwgMjkxLCAyODQsIDI3OCwgMjcxLCAyNjUsIDI1OSwgNTA3LCA0OTYsIDQ4NSwgNDc1LCA0NjUsIDQ1NiwgNDQ2LCA0MzcsIDQyOCwgNDIwLCA0MTIsIDQwNCwgMzk2LCAzODgsIDM4MSwgMzc0LCAzNjcsIDM2MCwgMzU0LCAzNDcsIDM0MSwgMzM1LCAzMjksIDMyMywgMzE4LCAzMTIsIDMwNywgMzAyLCAyOTcsIDI5MiwgMjg3LCAyODIsIDI3OCwgMjczLCAyNjksIDI2NSwgMjYxLCA1MTIsIDUwNSwgNDk3LCA0ODksIDQ4MiwgNDc1LCA0NjgsIDQ2MSwgNDU0LCA0NDcsIDQ0MSwgNDM1LCA0MjgsIDQyMiwgNDE3LCA0MTEsIDQwNSwgMzk5LCAzOTQsIDM4OSwgMzgzLCAzNzgsIDM3MywgMzY4LCAzNjQsIDM1OSwgMzU0LCAzNTAsIDM0NSwgMzQxLCAzMzcsIDMzMiwgMzI4LCAzMjQsIDMyMCwgMzE2LCAzMTIsIDMwOSwgMzA1LCAzMDEsIDI5OCwgMjk0LCAyOTEsIDI4NywgMjg0LCAyODEsIDI3OCwgMjc0LCAyNzEsIDI2OCwgMjY1LCAyNjIsIDI1OSwgMjU3LCA1MDcsIDUwMSwgNDk2LCA0OTEsIDQ4NSwgNDgwLCA0NzUsIDQ3MCwgNDY1LCA0NjAsIDQ1NiwgNDUxLCA0NDYsIDQ0MiwgNDM3LCA0MzMsIDQyOCwgNDI0LCA0MjAsIDQxNiwgNDEyLCA0MDgsIDQwNCwgNDAwLCAzOTYsIDM5MiwgMzg4LCAzODUsIDM4MSwgMzc3LCAzNzQsIDM3MCwgMzY3LCAzNjMsIDM2MCwgMzU3LCAzNTQsIDM1MCwgMzQ3LCAzNDQsIDM0MSwgMzM4LCAzMzUsIDMzMiwgMzI5LCAzMjYsIDMyMywgMzIwLCAzMTgsIDMxNSwgMzEyLCAzMTAsIDMwNywgMzA0LCAzMDIsIDI5OSwgMjk3LCAyOTQsIDI5MiwgMjg5LCAyODcsIDI4NSwgMjgyLCAyODAsIDI3OCwgMjc1LCAyNzMsIDI3MSwgMjY5LCAyNjcsIDI2NSwgMjYzLCAyNjEsIDI1OV07CiAgdmFyIHNoZ1RhYmxlID0gWzksIDExLCAxMiwgMTMsIDEzLCAxNCwgMTQsIDE1LCAxNSwgMTUsIDE1LCAxNiwgMTYsIDE2LCAxNiwgMTcsIDE3LCAxNywgMTcsIDE3LCAxNywgMTcsIDE4LCAxOCwgMTgsIDE4LCAxOCwgMTgsIDE4LCAxOCwgMTgsIDE5LCAxOSwgMTksIDE5LCAxOSwgMTksIDE5LCAxOSwgMTksIDE5LCAxOSwgMTksIDE5LCAxOSwgMjAsIDIwLCAyMCwgMjAsIDIwLCAyMCwgMjAsIDIwLCAyMCwgMjAsIDIwLCAyMCwgMjAsIDIwLCAyMCwgMjAsIDIwLCAyMCwgMjEsIDIxLCAyMSwgMjEsIDIxLCAyMSwgMjEsIDIxLCAyMSwgMjEsIDIxLCAyMSwgMjEsIDIxLCAyMSwgMjEsIDIxLCAyMSwgMjEsIDIxLCAyMSwgMjEsIDIxLCAyMSwgMjEsIDIxLCAyMSwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIyLCAyMiwgMjIsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDIzLCAyMywgMjMsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNCwgMjQsIDI0LCAyNF07CiAgLyoqDQogICAqIEBwYXJhbSB7SW1hZ2VEYXRhfSBpbWFnZURhdGENCiAgICogQHBhcmFtIHtJbnRlZ2VyfSB0b3BYDQogICAqIEBwYXJhbSB7SW50ZWdlcn0gdG9wWQ0KICAgKiBAcGFyYW0ge0ludGVnZXJ9IHdpZHRoDQogICAqIEBwYXJhbSB7SW50ZWdlcn0gaGVpZ2h0DQogICAqIEBwYXJhbSB7RmxvYXR9IHJhZGl1cw0KICAgKiBAcmV0dXJucyB7SW1hZ2VEYXRhfQ0KICAgKi8KCgogIGZ1bmN0aW9uIHByb2Nlc3NJbWFnZURhdGFSR0IoaW1hZ2VEYXRhLCB0b3BYLCB0b3BZLCB3aWR0aCwgaGVpZ2h0LCByYWRpdXMpIHsKICAgIHZhciBwaXhlbHMgPSBpbWFnZURhdGEgPyBpbWFnZURhdGEuZGF0YSA6IFtdOwogICAgdmFyIGRpdiA9IDIgKiByYWRpdXMgKyAxOyAvLyBjb25zdCB3NCA9IHdpZHRoIDw8IDI7CgogICAgdmFyIHdpZHRoTWludXMxID0gd2lkdGggLSAxOwogICAgdmFyIGhlaWdodE1pbnVzMSA9IGhlaWdodCAtIDE7CiAgICB2YXIgcmFkaXVzUGx1czEgPSByYWRpdXMgKyAxOwogICAgdmFyIHN1bUZhY3RvciA9IHJhZGl1c1BsdXMxICogKHJhZGl1c1BsdXMxICsgMSkgLyAyOwogICAgdmFyIHN0YWNrU3RhcnQgPSBuZXcgQmx1clN0YWNrKCk7CiAgICB2YXIgc3RhY2sgPSBzdGFja1N0YXJ0OwogICAgdmFyIHN0YWNrRW5kOwoKICAgIGZvciAodmFyIGkgPSAxOyBpIDwgZGl2OyBpKyspIHsKICAgICAgc3RhY2sgPSBzdGFjay5uZXh0ID0gbmV3IEJsdXJTdGFjaygpOwoKICAgICAgaWYgKGkgPT09IHJhZGl1c1BsdXMxKSB7CiAgICAgICAgc3RhY2tFbmQgPSBzdGFjazsKICAgICAgfQogICAgfQoKICAgIHN0YWNrLm5leHQgPSBzdGFja1N0YXJ0OwogICAgdmFyIHN0YWNrSW4gPSBudWxsOwogICAgdmFyIHN0YWNrT3V0ID0gbnVsbDsKICAgIHZhciBtdWxTdW0gPSBtdWxUYWJsZVtyYWRpdXNdOwogICAgdmFyIHNoZ1N1bSA9IHNoZ1RhYmxlW3JhZGl1c107CiAgICB2YXIgcCwgcmJzOwogICAgdmFyIHl3ID0gMCwKICAgICAgICB5aSA9IDA7CgogICAgZm9yICh2YXIgeSA9IDA7IHkgPCBoZWlnaHQ7IHkrKykgewogICAgICB2YXIgcHIgPSBwaXhlbHNbeWldLAogICAgICAgICAgcGcgPSBwaXhlbHNbeWkgKyAxXSwKICAgICAgICAgIHBiID0gcGl4ZWxzW3lpICsgMl0sCiAgICAgICAgICByT3V0U3VtID0gcmFkaXVzUGx1czEgKiBwciwKICAgICAgICAgIGdPdXRTdW0gPSByYWRpdXNQbHVzMSAqIHBnLAogICAgICAgICAgYk91dFN1bSA9IHJhZGl1c1BsdXMxICogcGIsCiAgICAgICAgICByU3VtID0gc3VtRmFjdG9yICogcHIsCiAgICAgICAgICBnU3VtID0gc3VtRmFjdG9yICogcGcsCiAgICAgICAgICBiU3VtID0gc3VtRmFjdG9yICogcGI7CiAgICAgIHN0YWNrID0gc3RhY2tTdGFydDsKCiAgICAgIGZvciAodmFyIF9pNSA9IDA7IF9pNSA8IHJhZGl1c1BsdXMxOyBfaTUrKykgewogICAgICAgIHN0YWNrLnIgPSBwcjsKICAgICAgICBzdGFjay5nID0gcGc7CiAgICAgICAgc3RhY2suYiA9IHBiOwogICAgICAgIHN0YWNrID0gc3RhY2submV4dDsKICAgICAgfQoKICAgICAgdmFyIHJJblN1bSA9IDAsCiAgICAgICAgICBnSW5TdW0gPSAwLAogICAgICAgICAgYkluU3VtID0gMDsKCiAgICAgIGZvciAodmFyIF9pNiA9IDE7IF9pNiA8IHJhZGl1c1BsdXMxOyBfaTYrKykgewogICAgICAgIHAgPSB5aSArICgod2lkdGhNaW51czEgPCBfaTYgPyB3aWR0aE1pbnVzMSA6IF9pNikgPDwgMik7CiAgICAgICAgclN1bSArPSAoc3RhY2suciA9IHByID0gcGl4ZWxzW3BdKSAqIChyYnMgPSByYWRpdXNQbHVzMSAtIF9pNik7CiAgICAgICAgZ1N1bSArPSAoc3RhY2suZyA9IHBnID0gcGl4ZWxzW3AgKyAxXSkgKiByYnM7CiAgICAgICAgYlN1bSArPSAoc3RhY2suYiA9IHBiID0gcGl4ZWxzW3AgKyAyXSkgKiByYnM7CiAgICAgICAgckluU3VtICs9IHByOwogICAgICAgIGdJblN1bSArPSBwZzsKICAgICAgICBiSW5TdW0gKz0gcGI7CiAgICAgICAgc3RhY2sgPSBzdGFjay5uZXh0OwogICAgICB9CgogICAgICBzdGFja0luID0gc3RhY2tTdGFydDsKICAgICAgc3RhY2tPdXQgPSBzdGFja0VuZDsKCiAgICAgIGZvciAodmFyIHggPSAwOyB4IDwgd2lkdGg7IHgrKykgewogICAgICAgIHBpeGVsc1t5aV0gPSByU3VtICogbXVsU3VtID4+IHNoZ1N1bTsKICAgICAgICBwaXhlbHNbeWkgKyAxXSA9IGdTdW0gKiBtdWxTdW0gPj4gc2hnU3VtOwogICAgICAgIHBpeGVsc1t5aSArIDJdID0gYlN1bSAqIG11bFN1bSA+PiBzaGdTdW07CiAgICAgICAgclN1bSAtPSByT3V0U3VtOwogICAgICAgIGdTdW0gLT0gZ091dFN1bTsKICAgICAgICBiU3VtIC09IGJPdXRTdW07CiAgICAgICAgck91dFN1bSAtPSBzdGFja0luLnI7CiAgICAgICAgZ091dFN1bSAtPSBzdGFja0luLmc7CiAgICAgICAgYk91dFN1bSAtPSBzdGFja0luLmI7CiAgICAgICAgcCA9IHl3ICsgKChwID0geCArIHJhZGl1cyArIDEpIDwgd2lkdGhNaW51czEgPyBwIDogd2lkdGhNaW51czEpIDw8IDI7CiAgICAgICAgckluU3VtICs9IHN0YWNrSW4uciA9IHBpeGVsc1twXTsKICAgICAgICBnSW5TdW0gKz0gc3RhY2tJbi5nID0gcGl4ZWxzW3AgKyAxXTsKICAgICAgICBiSW5TdW0gKz0gc3RhY2tJbi5iID0gcGl4ZWxzW3AgKyAyXTsKICAgICAgICByU3VtICs9IHJJblN1bTsKICAgICAgICBnU3VtICs9IGdJblN1bTsKICAgICAgICBiU3VtICs9IGJJblN1bTsKICAgICAgICBzdGFja0luID0gc3RhY2tJbi5uZXh0OwogICAgICAgIHJPdXRTdW0gKz0gcHIgPSBzdGFja091dC5yOwogICAgICAgIGdPdXRTdW0gKz0gcGcgPSBzdGFja091dC5nOwogICAgICAgIGJPdXRTdW0gKz0gcGIgPSBzdGFja091dC5iOwogICAgICAgIHJJblN1bSAtPSBwcjsKICAgICAgICBnSW5TdW0gLT0gcGc7CiAgICAgICAgYkluU3VtIC09IHBiOwogICAgICAgIHN0YWNrT3V0ID0gc3RhY2tPdXQubmV4dDsKICAgICAgICB5aSArPSA0OwogICAgICB9CgogICAgICB5dyArPSB3aWR0aDsKICAgIH0KCiAgICBmb3IgKHZhciBfeDIgPSAwOyBfeDIgPCB3aWR0aDsgX3gyKyspIHsKICAgICAgeWkgPSBfeDIgPDwgMjsKCiAgICAgIHZhciBfcHIyID0gcGl4ZWxzW3lpXSwKICAgICAgICAgIF9wZzIgPSBwaXhlbHNbeWkgKyAxXSwKICAgICAgICAgIF9wYjIgPSBwaXhlbHNbeWkgKyAyXSwKICAgICAgICAgIF9yT3V0U3VtMiA9IHJhZGl1c1BsdXMxICogX3ByMiwKICAgICAgICAgIF9nT3V0U3VtMiA9IHJhZGl1c1BsdXMxICogX3BnMiwKICAgICAgICAgIF9iT3V0U3VtMiA9IHJhZGl1c1BsdXMxICogX3BiMiwKICAgICAgICAgIF9yU3VtMiA9IHN1bUZhY3RvciAqIF9wcjIsCiAgICAgICAgICBfZ1N1bTIgPSBzdW1GYWN0b3IgKiBfcGcyLAogICAgICAgICAgX2JTdW0yID0gc3VtRmFjdG9yICogX3BiMjsKCiAgICAgIHN0YWNrID0gc3RhY2tTdGFydDsKCiAgICAgIGZvciAodmFyIF9pNyA9IDA7IF9pNyA8IHJhZGl1c1BsdXMxOyBfaTcrKykgewogICAgICAgIHN0YWNrLnIgPSBfcHIyOwogICAgICAgIHN0YWNrLmcgPSBfcGcyOwogICAgICAgIHN0YWNrLmIgPSBfcGIyOwogICAgICAgIHN0YWNrID0gc3RhY2submV4dDsKICAgICAgfQoKICAgICAgdmFyIF9ySW5TdW0yID0gMCwKICAgICAgICAgIF9nSW5TdW0yID0gMCwKICAgICAgICAgIF9iSW5TdW0yID0gMDsKCiAgICAgIGZvciAodmFyIF9pOCA9IDEsIHlwID0gd2lkdGg7IF9pOCA8PSByYWRpdXM7IF9pOCsrKSB7CiAgICAgICAgeWkgPSB5cCArIF94MiA8PCAyOwogICAgICAgIF9yU3VtMiArPSAoc3RhY2suciA9IF9wcjIgPSBwaXhlbHNbeWldKSAqIChyYnMgPSByYWRpdXNQbHVzMSAtIF9pOCk7CiAgICAgICAgX2dTdW0yICs9IChzdGFjay5nID0gX3BnMiA9IHBpeGVsc1t5aSArIDFdKSAqIHJiczsKICAgICAgICBfYlN1bTIgKz0gKHN0YWNrLmIgPSBfcGIyID0gcGl4ZWxzW3lpICsgMl0pICogcmJzOwogICAgICAgIF9ySW5TdW0yICs9IF9wcjI7CiAgICAgICAgX2dJblN1bTIgKz0gX3BnMjsKICAgICAgICBfYkluU3VtMiArPSBfcGIyOwogICAgICAgIHN0YWNrID0gc3RhY2submV4dDsKCiAgICAgICAgaWYgKF9pOCA8IGhlaWdodE1pbnVzMSkgewogICAgICAgICAgeXAgKz0gd2lkdGg7CiAgICAgICAgfQogICAgICB9CgogICAgICB5aSA9IF94MjsKICAgICAgc3RhY2tJbiA9IHN0YWNrU3RhcnQ7CiAgICAgIHN0YWNrT3V0ID0gc3RhY2tFbmQ7CgogICAgICBmb3IgKHZhciBfeTIgPSAwOyBfeTIgPCBoZWlnaHQ7IF95MisrKSB7CiAgICAgICAgcCA9IHlpIDw8IDI7CiAgICAgICAgcGl4ZWxzW3BdID0gX3JTdW0yICogbXVsU3VtID4+IHNoZ1N1bTsKICAgICAgICBwaXhlbHNbcCArIDFdID0gX2dTdW0yICogbXVsU3VtID4+IHNoZ1N1bTsKICAgICAgICBwaXhlbHNbcCArIDJdID0gX2JTdW0yICogbXVsU3VtID4+IHNoZ1N1bTsKICAgICAgICBfclN1bTIgLT0gX3JPdXRTdW0yOwogICAgICAgIF9nU3VtMiAtPSBfZ091dFN1bTI7CiAgICAgICAgX2JTdW0yIC09IF9iT3V0U3VtMjsKICAgICAgICBfck91dFN1bTIgLT0gc3RhY2tJbi5yOwogICAgICAgIF9nT3V0U3VtMiAtPSBzdGFja0luLmc7CiAgICAgICAgX2JPdXRTdW0yIC09IHN0YWNrSW4uYjsKICAgICAgICBwID0gX3gyICsgKChwID0gX3kyICsgcmFkaXVzUGx1czEpIDwgaGVpZ2h0TWludXMxID8gcCA6IGhlaWdodE1pbnVzMSkgKiB3aWR0aCA8PCAyOwogICAgICAgIF9yU3VtMiArPSBfckluU3VtMiArPSBzdGFja0luLnIgPSBwaXhlbHNbcF07CiAgICAgICAgX2dTdW0yICs9IF9nSW5TdW0yICs9IHN0YWNrSW4uZyA9IHBpeGVsc1twICsgMV07CiAgICAgICAgX2JTdW0yICs9IF9iSW5TdW0yICs9IHN0YWNrSW4uYiA9IHBpeGVsc1twICsgMl07CiAgICAgICAgc3RhY2tJbiA9IHN0YWNrSW4ubmV4dDsKICAgICAgICBfck91dFN1bTIgKz0gX3ByMiA9IHN0YWNrT3V0LnI7CiAgICAgICAgX2dPdXRTdW0yICs9IF9wZzIgPSBzdGFja091dC5nOwogICAgICAgIF9iT3V0U3VtMiArPSBfcGIyID0gc3RhY2tPdXQuYjsKICAgICAgICBfckluU3VtMiAtPSBfcHIyOwogICAgICAgIF9nSW5TdW0yIC09IF9wZzI7CiAgICAgICAgX2JJblN1bTIgLT0gX3BiMjsKICAgICAgICBzdGFja091dCA9IHN0YWNrT3V0Lm5leHQ7CiAgICAgICAgeWkgKz0gd2lkdGg7CiAgICAgIH0KICAgIH0KCiAgICByZXR1cm4gaW1hZ2VEYXRhOwogIH0KCiAgb25tZXNzYWdlID0gZnVuY3Rpb24gb25tZXNzYWdlKGUpIHsKICAgIHZhciBkYXRhID0gZS5kYXRhOwogICAgdmFyIGltYWdlRGF0YTsKCiAgICB0cnkgewogICAgICBpbWFnZURhdGEgPSBwcm9jZXNzSW1hZ2VEYXRhUkdCKGRhdGEuaW1hZ2VEYXRhLCBkYXRhLnRvcFgsIGRhdGEudG9wWSwgZGF0YS53aWR0aCwgZGF0YS5oZWlnaHQsIGRhdGEucmFkaXVzKTsKICAgICAgcG9zdE1lc3NhZ2UoaW1hZ2VEYXRhKTsKICAgIH0gY2F0Y2ggKGUpIHt9CiAgfTsKCn0pKCk7Cgo=', null, false); /* eslint-enable */ var WorkerFactory$1 = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIGZ1bmN0aW9uIHBhcnNlKHN0cmluZywgZW1wdHkpIHsKICAgICAgdmFyIGpzb24gPSBlbXB0eSB8fCB7fTsKCiAgICAgIGlmIChzdHJpbmcpIHsKICAgICAgICB0cnkgewogICAgICAgICAganNvbiA9IEpTT04ucGFyc2Uoc3RyaW5nKTsKICAgICAgICB9IGNhdGNoIChlKSB7fQogICAgICB9CgogICAgICByZXR1cm4ganNvbjsKICAgIH0KCiAgICBmdW5jdGlvbiBzdHJpbmdpZnkoZGF0YSkgewogICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkoZGF0YSk7CiAgICB9CgogICAgb25tZXNzYWdlID0gZnVuY3Rpb24gb25tZXNzYWdlKGUpIHsKICAgICAgdmFyIG1zZyA9IGUuZGF0YTsKCiAgICAgIGlmIChtc2cudHlwZSA9PSAnc3RyaW5naWZ5JykgewogICAgICAgIHBvc3RNZXNzYWdlKHN0cmluZ2lmeShtc2cuZGF0YSkpOwogICAgICB9IGVsc2UgaWYgKG1zZy50eXBlID09ICdwYXJzZScpIHsKICAgICAgICBwb3N0TWVzc2FnZShwYXJzZShtc2cuZGF0YSwgbXNnLmVtcHR5KSk7CiAgICAgIH0KICAgIH07Cgp9KSgpOwoK', null, false); /* eslint-enable */ var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIG9ubWVzc2FnZSA9IGZ1bmN0aW9uIG9ubWVzc2FnZShlKSB7CiAgICAgIHZhciBtc2cgPSBlLmRhdGE7CgogICAgICBpZiAobXNnLnR5cGUgPT0gJ2FjY291bnRfYm9va21hcmtzX3BhcnNlJykgewogICAgICAgIHZhciBib29rbWFya3MgPSBtc2cuZGF0YS5tYXAoZnVuY3Rpb24gKGVsZW0pIHsKICAgICAgICAgIGlmICh0eXBlb2YgZWxlbS5kYXRhID09ICdzdHJpbmcnKSB7CiAgICAgICAgICAgIGVsZW0uZGF0YSA9IEpTT04ucGFyc2UoZWxlbS5kYXRhKTsKICAgICAgICAgICAgZGVsZXRlIGVsZW0uZGF0YS5yZWxlYXNlX3F1YWxpdHk7CiAgICAgICAgICAgIGRlbGV0ZSBlbGVtLmRhdGEucXVhbGl0eTsKICAgICAgICAgIH0KCiAgICAgICAgICByZXR1cm4gZWxlbTsKICAgICAgICB9KS5yZXZlcnNlKCk7CiAgICAgICAgcG9zdE1lc3NhZ2UoYm9va21hcmtzKTsKICAgICAgfQogICAgfTsKCn0pKCk7Cgo=', null, false); /* eslint-enable */ var agent$1 = navigator.userAgent.toLowerCase(); function WebWorker(worker, nosuport) { var callback = false; var timer; worker.onmessage = function (data) { clearTimeout(timer); if (callback) { callback(data); callback = false; } }; this.call = function (data, call) { callback = call; worker.postMessage(data); timer = setTimeout(function () { nosuport.call(data, call); }, 1500); }; } function createWorker(extend, nosuport) { var worker; if (agent$1.indexOf("netcast") > -1 || agent$1.indexOf("maple") > -1) return nosuport || { call: function call() {} }; try { worker = new WebWorker(new extend(), nosuport || { call: function call() {} }); } catch (e) { worker = nosuport || { call: function call() {} }; } return worker; } var blurWorker = createWorker(WorkerFactory$2, { call: function call(msg, _call) { var imageData = Blur.imageDataRGB(msg.imageData, msg.topX, msg.topY, msg.width, msg.height, msg.radius); _call({ data: imageData }); } }); var jsonWorker = createWorker(WorkerFactory$1, { call: function call(msg, _call2) { _call2({ data: msg.type == 'parse' ? Arrays.decodeJson(msg.data, msg.empty) : JSON.stringify(msg.data) }); } }); var utilsWorker = createWorker(WorkerFactory, { call: function call(msg, _call3) { if (msg.type == 'account_bookmarks_parse') { var bookmarks = msg.data.map(function (elem) { if (typeof elem.data == 'string') { elem.data = JSON.parse(elem.data); delete elem.data.release_quality; delete elem.data.quality; } return elem; }).reverse(); _call3({ data: bookmarks }); } } }); var AppWorker = { blur: blurWorker.call, json: jsonWorker.call, utils: utilsWorker.call }; /* eslint-disable no-bitwise -- used for calculations */ /* eslint-disable unicorn/prefer-query-selector -- aiming at backward-compatibility */ /** * StackBlur - a fast almost Gaussian Blur For Canvas * * In case you find this class useful - especially in commercial projects - * I am not totally unhappy for a small donation to my PayPal account * mario@quasimondo.de * * Or support me on flattr: * {@link https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript}. * * @module StackBlur * @author Mario Klingemann * Contact: mario@quasimondo.com * Website: {@link http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html} * Twitter: @quasimondo * * @copyright (c) 2010 Mario Klingemann * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ var mulTable = [512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259]; var shgTable = [9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24]; /** * @param {string|HTMLImageElement} img * @param {string|HTMLCanvasElement} canvas * @param {Float} radius * @param {boolean} blurAlphaChannel * @param {boolean} useOffset * @param {boolean} skipStyles * @returns {undefined} */ function processImage(img, canvas, radius, blurAlphaChannel, useOffset, skipStyles) { if (typeof img === 'string') { img = document.getElementById(img); } if (!img || !('naturalWidth' in img)) { return; } var dimensionType = useOffset ? 'offset' : 'natural'; var w = img[dimensionType + 'Width']; var h = img[dimensionType + 'Height']; if (typeof canvas === 'string') { canvas = document.getElementById(canvas); } if (!canvas || !('getContext' in canvas)) { return; } if (!skipStyles) { canvas.style.width = w + 'px'; canvas.style.height = h + 'px'; } canvas.width = w; canvas.height = h; var context = canvas.getContext('2d'); context.clearRect(0, 0, w, h); context.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, 0, 0, w, h); if (isNaN(radius) || radius < 1) { return; } if (blurAlphaChannel) { processCanvasRGBA(canvas, 0, 0, w, h, radius); } else { processCanvasRGB(canvas, 0, 0, w, h, radius); } } /** * @param {string|HTMLCanvasElement} canvas * @param {Integer} topX * @param {Integer} topY * @param {Integer} width * @param {Integer} height * @throws {Error|TypeError} * @returns {ImageData} See {@link https://html.spec.whatwg.org/multipage/canvas.html#imagedata} */ function getImageDataFromCanvas(canvas, topX, topY, width, height) { if (typeof canvas === 'string') { canvas = document.getElementById(canvas); } if (!canvas || _typeof(canvas) !== 'object' || !('getContext' in canvas)) ; var context = canvas.getContext('2d'); try { return context.getImageData(topX, topY, width, height); } catch (e) {//throw new Error('unable to access image data: ' + e); } } /** * @param {HTMLCanvasElement} canvas * @param {Integer} topX * @param {Integer} topY * @param {Integer} width * @param {Integer} height * @param {Float} radius * @returns {undefined} */ function processCanvasRGBA(canvas, topX, topY, width, height, radius) { if (isNaN(radius) || radius < 1) { return; } radius |= 0; var imageData = getImageDataFromCanvas(canvas, topX, topY, width, height); if (imageData) { imageData = processImageDataRGBA(imageData, topX, topY, width, height, radius); try { canvas.getContext('2d').putImageData(imageData, topX, topY); } catch (e) {} } } /** * @param {ImageData} imageData * @param {Integer} topX * @param {Integer} topY * @param {Integer} width * @param {Integer} height * @param {Float} radius * @returns {ImageData} */ function processImageDataRGBA(imageData, topX, topY, width, height, radius) { var pixels = imageData ? imageData.data : []; var div = 2 * radius + 1; // const w4 = width << 2; var widthMinus1 = width - 1; var heightMinus1 = height - 1; var radiusPlus1 = radius + 1; var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; var stackStart = new BlurStack(); var stack = stackStart; var stackEnd; for (var i = 1; i < div; i++) { stack = stack.next = new BlurStack(); if (i === radiusPlus1) { stackEnd = stack; } } stack.next = stackStart; var stackIn = null, stackOut = null, yw = 0, yi = 0; var mulSum = mulTable[radius]; var shgSum = shgTable[radius]; for (var y = 0; y < height; y++) { stack = stackStart; var pr = pixels[yi], pg = pixels[yi + 1], pb = pixels[yi + 2], pa = pixels[yi + 3]; for (var _i = 0; _i < radiusPlus1; _i++) { stack.r = pr; stack.g = pg; stack.b = pb; stack.a = pa; stack = stack.next; } var rInSum = 0, gInSum = 0, bInSum = 0, aInSum = 0, rOutSum = radiusPlus1 * pr, gOutSum = radiusPlus1 * pg, bOutSum = radiusPlus1 * pb, aOutSum = radiusPlus1 * pa, rSum = sumFactor * pr, gSum = sumFactor * pg, bSum = sumFactor * pb, aSum = sumFactor * pa; for (var _i2 = 1; _i2 < radiusPlus1; _i2++) { var p = yi + ((widthMinus1 < _i2 ? widthMinus1 : _i2) << 2); var r = pixels[p], g = pixels[p + 1], b = pixels[p + 2], a = pixels[p + 3]; var rbs = radiusPlus1 - _i2; rSum += (stack.r = r) * rbs; gSum += (stack.g = g) * rbs; bSum += (stack.b = b) * rbs; aSum += (stack.a = a) * rbs; rInSum += r; gInSum += g; bInSum += b; aInSum += a; stack = stack.next; } stackIn = stackStart; stackOut = stackEnd; for (var x = 0; x < width; x++) { var paInitial = aSum * mulSum >> shgSum; pixels[yi + 3] = paInitial; if (paInitial !== 0) { var _a2 = 255 / paInitial; pixels[yi] = (rSum * mulSum >> shgSum) * _a2; pixels[yi + 1] = (gSum * mulSum >> shgSum) * _a2; pixels[yi + 2] = (bSum * mulSum >> shgSum) * _a2; } else { pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0; } rSum -= rOutSum; gSum -= gOutSum; bSum -= bOutSum; aSum -= aOutSum; rOutSum -= stackIn.r; gOutSum -= stackIn.g; bOutSum -= stackIn.b; aOutSum -= stackIn.a; var _p = x + radius + 1; _p = yw + (_p < widthMinus1 ? _p : widthMinus1) << 2; rInSum += stackIn.r = pixels[_p]; gInSum += stackIn.g = pixels[_p + 1]; bInSum += stackIn.b = pixels[_p + 2]; aInSum += stackIn.a = pixels[_p + 3]; rSum += rInSum; gSum += gInSum; bSum += bInSum; aSum += aInSum; stackIn = stackIn.next; var _stackOut = stackOut, _r = _stackOut.r, _g = _stackOut.g, _b = _stackOut.b, _a = _stackOut.a; rOutSum += _r; gOutSum += _g; bOutSum += _b; aOutSum += _a; rInSum -= _r; gInSum -= _g; bInSum -= _b; aInSum -= _a; stackOut = stackOut.next; yi += 4; } yw += width; } for (var _x = 0; _x < width; _x++) { yi = _x << 2; var _pr = pixels[yi], _pg = pixels[yi + 1], _pb = pixels[yi + 2], _pa = pixels[yi + 3], _rOutSum = radiusPlus1 * _pr, _gOutSum = radiusPlus1 * _pg, _bOutSum = radiusPlus1 * _pb, _aOutSum = radiusPlus1 * _pa, _rSum = sumFactor * _pr, _gSum = sumFactor * _pg, _bSum = sumFactor * _pb, _aSum = sumFactor * _pa; stack = stackStart; for (var _i3 = 0; _i3 < radiusPlus1; _i3++) { stack.r = _pr; stack.g = _pg; stack.b = _pb; stack.a = _pa; stack = stack.next; } var yp = width; var _gInSum = 0, _bInSum = 0, _aInSum = 0, _rInSum = 0; for (var _i4 = 1; _i4 <= radius; _i4++) { yi = yp + _x << 2; var _rbs = radiusPlus1 - _i4; _rSum += (stack.r = _pr = pixels[yi]) * _rbs; _gSum += (stack.g = _pg = pixels[yi + 1]) * _rbs; _bSum += (stack.b = _pb = pixels[yi + 2]) * _rbs; _aSum += (stack.a = _pa = pixels[yi + 3]) * _rbs; _rInSum += _pr; _gInSum += _pg; _bInSum += _pb; _aInSum += _pa; stack = stack.next; if (_i4 < heightMinus1) { yp += width; } } yi = _x; stackIn = stackStart; stackOut = stackEnd; for (var _y = 0; _y < height; _y++) { var _p2 = yi << 2; pixels[_p2 + 3] = _pa = _aSum * mulSum >> shgSum; if (_pa > 0) { _pa = 255 / _pa; pixels[_p2] = (_rSum * mulSum >> shgSum) * _pa; pixels[_p2 + 1] = (_gSum * mulSum >> shgSum) * _pa; pixels[_p2 + 2] = (_bSum * mulSum >> shgSum) * _pa; } else { pixels[_p2] = pixels[_p2 + 1] = pixels[_p2 + 2] = 0; } _rSum -= _rOutSum; _gSum -= _gOutSum; _bSum -= _bOutSum; _aSum -= _aOutSum; _rOutSum -= stackIn.r; _gOutSum -= stackIn.g; _bOutSum -= stackIn.b; _aOutSum -= stackIn.a; _p2 = _x + ((_p2 = _y + radiusPlus1) < heightMinus1 ? _p2 : heightMinus1) * width << 2; _rSum += _rInSum += stackIn.r = pixels[_p2]; _gSum += _gInSum += stackIn.g = pixels[_p2 + 1]; _bSum += _bInSum += stackIn.b = pixels[_p2 + 2]; _aSum += _aInSum += stackIn.a = pixels[_p2 + 3]; stackIn = stackIn.next; _rOutSum += _pr = stackOut.r; _gOutSum += _pg = stackOut.g; _bOutSum += _pb = stackOut.b; _aOutSum += _pa = stackOut.a; _rInSum -= _pr; _gInSum -= _pg; _bInSum -= _pb; _aInSum -= _pa; stackOut = stackOut.next; yi += width; } } return imageData; } /** * @param {HTMLCanvasElement} canvas * @param {Integer} topX * @param {Integer} topY * @param {Integer} width * @param {Integer} height * @param {Float} radius * @returns {undefined} */ function processCanvasRGB(canvas, topX, topY, width, height, radius, callback) { if (isNaN(radius) || radius < 1) { return; } radius |= 0; var imageData = getImageDataFromCanvas(canvas, topX, topY, width, height); AppWorker.blur({ imageData: imageData, topX: topX, topY: topY, width: width, height: height, radius: radius }, function (imageBlurData) { try { canvas.getContext('2d').putImageData(imageBlurData.data, topX, topY); } catch (e) {} callback(); }); /* imageData = processImageDataRGB( imageData, topX, topY, width, height, radius ); try{ canvas.getContext('2d').putImageData(imageData, topX, topY); } catch(e){} */ } /** * @param {ImageData} imageData * @param {Integer} topX * @param {Integer} topY * @param {Integer} width * @param {Integer} height * @param {Float} radius * @returns {ImageData} */ function processImageDataRGB(imageData, topX, topY, width, height, radius) { var pixels = imageData ? imageData.data : []; var div = 2 * radius + 1; // const w4 = width << 2; var widthMinus1 = width - 1; var heightMinus1 = height - 1; var radiusPlus1 = radius + 1; var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; var stackStart = new BlurStack(); var stack = stackStart; var stackEnd; for (var i = 1; i < div; i++) { stack = stack.next = new BlurStack(); if (i === radiusPlus1) { stackEnd = stack; } } stack.next = stackStart; var stackIn = null; var stackOut = null; var mulSum = mulTable[radius]; var shgSum = shgTable[radius]; var p, rbs; var yw = 0, yi = 0; for (var y = 0; y < height; y++) { var pr = pixels[yi], pg = pixels[yi + 1], pb = pixels[yi + 2], rOutSum = radiusPlus1 * pr, gOutSum = radiusPlus1 * pg, bOutSum = radiusPlus1 * pb, rSum = sumFactor * pr, gSum = sumFactor * pg, bSum = sumFactor * pb; stack = stackStart; for (var _i5 = 0; _i5 < radiusPlus1; _i5++) { stack.r = pr; stack.g = pg; stack.b = pb; stack = stack.next; } var rInSum = 0, gInSum = 0, bInSum = 0; for (var _i6 = 1; _i6 < radiusPlus1; _i6++) { p = yi + ((widthMinus1 < _i6 ? widthMinus1 : _i6) << 2); rSum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - _i6); gSum += (stack.g = pg = pixels[p + 1]) * rbs; bSum += (stack.b = pb = pixels[p + 2]) * rbs; rInSum += pr; gInSum += pg; bInSum += pb; stack = stack.next; } stackIn = stackStart; stackOut = stackEnd; for (var x = 0; x < width; x++) { pixels[yi] = rSum * mulSum >> shgSum; pixels[yi + 1] = gSum * mulSum >> shgSum; pixels[yi + 2] = bSum * mulSum >> shgSum; rSum -= rOutSum; gSum -= gOutSum; bSum -= bOutSum; rOutSum -= stackIn.r; gOutSum -= stackIn.g; bOutSum -= stackIn.b; p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2; rInSum += stackIn.r = pixels[p]; gInSum += stackIn.g = pixels[p + 1]; bInSum += stackIn.b = pixels[p + 2]; rSum += rInSum; gSum += gInSum; bSum += bInSum; stackIn = stackIn.next; rOutSum += pr = stackOut.r; gOutSum += pg = stackOut.g; bOutSum += pb = stackOut.b; rInSum -= pr; gInSum -= pg; bInSum -= pb; stackOut = stackOut.next; yi += 4; } yw += width; } for (var _x2 = 0; _x2 < width; _x2++) { yi = _x2 << 2; var _pr2 = pixels[yi], _pg2 = pixels[yi + 1], _pb2 = pixels[yi + 2], _rOutSum2 = radiusPlus1 * _pr2, _gOutSum2 = radiusPlus1 * _pg2, _bOutSum2 = radiusPlus1 * _pb2, _rSum2 = sumFactor * _pr2, _gSum2 = sumFactor * _pg2, _bSum2 = sumFactor * _pb2; stack = stackStart; for (var _i7 = 0; _i7 < radiusPlus1; _i7++) { stack.r = _pr2; stack.g = _pg2; stack.b = _pb2; stack = stack.next; } var _rInSum2 = 0, _gInSum2 = 0, _bInSum2 = 0; for (var _i8 = 1, yp = width; _i8 <= radius; _i8++) { yi = yp + _x2 << 2; _rSum2 += (stack.r = _pr2 = pixels[yi]) * (rbs = radiusPlus1 - _i8); _gSum2 += (stack.g = _pg2 = pixels[yi + 1]) * rbs; _bSum2 += (stack.b = _pb2 = pixels[yi + 2]) * rbs; _rInSum2 += _pr2; _gInSum2 += _pg2; _bInSum2 += _pb2; stack = stack.next; if (_i8 < heightMinus1) { yp += width; } } yi = _x2; stackIn = stackStart; stackOut = stackEnd; for (var _y2 = 0; _y2 < height; _y2++) { p = yi << 2; pixels[p] = _rSum2 * mulSum >> shgSum; pixels[p + 1] = _gSum2 * mulSum >> shgSum; pixels[p + 2] = _bSum2 * mulSum >> shgSum; _rSum2 -= _rOutSum2; _gSum2 -= _gOutSum2; _bSum2 -= _bOutSum2; _rOutSum2 -= stackIn.r; _gOutSum2 -= stackIn.g; _bOutSum2 -= stackIn.b; p = _x2 + ((p = _y2 + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2; _rSum2 += _rInSum2 += stackIn.r = pixels[p]; _gSum2 += _gInSum2 += stackIn.g = pixels[p + 1]; _bSum2 += _bInSum2 += stackIn.b = pixels[p + 2]; stackIn = stackIn.next; _rOutSum2 += _pr2 = stackOut.r; _gOutSum2 += _pg2 = stackOut.g; _bOutSum2 += _pb2 = stackOut.b; _rInSum2 -= _pr2; _gInSum2 -= _pg2; _bInSum2 -= _pb2; stackOut = stackOut.next; yi += width; } } return imageData; } /** * */ var BlurStack = /** * Set properties. */ function BlurStack() { _classCallCheck(this, BlurStack); this.r = 0; this.g = 0; this.b = 0; this.a = 0; this.next = null; }; var Blur = { /** * @function module:StackBlur.image * @see module:StackBlur~processImage */ image: processImage, /** * @function module:StackBlur.canvasRGBA * @see module:StackBlur~processCanvasRGBA */ canvasRGBA: processCanvasRGBA, /** * @function module:StackBlur.canvasRGB * @see module:StackBlur~processCanvasRGB */ canvasRGB: processCanvasRGB, /** * @function module:StackBlur.imageDataRGBA * @see module:StackBlur~processImageDataRGBA */ imageDataRGBA: processImageDataRGBA, /** * @function module:StackBlur.imageDataRGB * @see module:StackBlur~processImageDataRGB */ imageDataRGB: processImageDataRGB }; var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); canvas.width = 30; canvas.height = 17; var canvas_poster = document.createElement('canvas'), ctx_poster = canvas_poster.getContext('2d'); function extract$2(img_data) { var data = img_data.data, colors = []; for (var i = 0, n = data.length; i < n; i += 4) { colors.push([data[i], data[i + 1], data[i + 2]]); } return colors; } function palette(palette) { var colors = { bright: [0, 0, 0], average: [127, 127, 127], dark: [255, 255, 255] }; var ar = 0, ag = 0, ab = 0, at = palette.length; var bg = 0, dk = 765; for (var i = 0; i < palette.length; i++) { var p = palette[i], a = p[0] + p[1] + p[2]; ar += p[0]; ag += p[1]; ab += p[2]; if (a > bg) { bg = a; colors.bright = p; } if (a < dk) { dk = a; colors.dark = p; } } colors.average = [Math.round(ar / at), Math.round(ag / at), Math.round(ab / at)]; return colors; } function rgba(c) { var o = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; return 'rgba(' + c.join(',') + ',' + o + ')'; } function tone(c) { var o = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var s = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 30; var l = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 80; var hls = rgbToHsl(c[0], c[1], c[2]); var rgb = hslToRgb$1(hls[0], Math.min(s, hls[1]), l); return rgba(rgb, o); } /** * Converts an RGB color value to HSL. * * @param {number} r The red color value * @param {number} g The green color value * @param {number} b The blue color value * @return {Array} The HSL representation */ function rgbToHsl(r, g, b) { var rabs, gabs, babs, rr, gg, bb, h, s, v, diff, diffc, percentRoundFn; rabs = r / 255; gabs = g / 255; babs = b / 255; v = Math.max(rabs, gabs, babs), diff = v - Math.min(rabs, gabs, babs); diffc = function diffc(c) { return (v - c) / 6 / diff + 1 / 2; }; percentRoundFn = function percentRoundFn(num) { return Math.round(num * 100) / 100; }; if (diff == 0) { h = s = 0; } else { s = diff / v; rr = diffc(rabs); gg = diffc(gabs); bb = diffc(babs); if (rabs === v) { h = bb - gg; } else if (gabs === v) { h = 1 / 3 + rr - bb; } else if (babs === v) { h = 2 / 3 + gg - rr; } if (h < 0) { h += 1; } else if (h > 1) { h -= 1; } } return [Math.round(h * 360), percentRoundFn(s * 100), percentRoundFn(v * 100)]; } /** * Converts an HSL color value to RGB. * * @param {number} h The hue * @param {number} s The saturation * @param {number} l The lightness * @return {Array} The RGB representation */ function hslToRgb$1(h, s, l) { s /= 100; l /= 100; var C = (1 - Math.abs(2 * l - 1)) * s; var hue = h / 60; var X = C * (1 - Math.abs(hue % 2 - 1)); var r = 0, g = 0, b = 0; if (hue >= 0 && hue < 1) { r = C; g = X; } else if (hue >= 1 && hue < 2) { r = X; g = C; } else if (hue >= 2 && hue < 3) { g = C; b = X; } else if (hue >= 3 && hue < 4) { g = X; b = C; } else if (hue >= 4 && hue < 5) { r = X; b = C; } else { r = C; b = X; } var m = l - C / 2; r += m; g += m; b += m; r *= 255.0; g *= 255.0; b *= 255.0; return [Math.round(r), Math.round(g), Math.round(b)]; } function reset$2(width, height) { canvas.width = width; canvas.height = height; } function get$f(img) { reset$2(30, 17); var ratio = Math.max(canvas.width / img.width, canvas.height / img.height); var nw = img.width * ratio, nh = img.height * ratio; ctx.drawImage(img, -(nw - canvas.width) / 2, -(nh - canvas.height) / 2, nw, nh); return extract$2(ctx.getImageData(0, 0, canvas.width, canvas.height)); } function blur$1(img, callback) { reset$2(200, 130); var ratio = Math.max(canvas.width / img.width, canvas.height / img.height); var nw = img.width * ratio, nh = img.height * ratio; ctx.drawImage(img, -(nw - canvas.width) / 2, -(nh - canvas.height) / 2, nw, nh); Blur.canvasRGB(canvas, 0, 0, canvas.width, canvas.height, 80, function () { var nimg = new Image(); try { nimg.src = canvas.toDataURL(); } catch (e) {} setTimeout(function () { callback(nimg); }, 100); }); } function getImg(callback) { var im = new Image(); try { im.src = canvas_poster.toDataURL(); } catch (e) {} setTimeout(function () { callback(im); }, 100); } function blurPoster(img, w, h, callback) { canvas_poster.width = w; canvas_poster.height = h; var ratio = Math.max(canvas_poster.width / img.width, canvas_poster.height / img.height); var nw = img.width * ratio, nh = img.height * ratio; setTimeout(function () { ctx_poster.drawImage(img, -(nw - canvas_poster.width) / 2, -(nh - canvas_poster.height) / 2, nw, nh); Blur.canvasRGB(canvas_poster, 0, 0, canvas_poster.width, canvas_poster.height, 50, function () { var gradient = ctx_poster.createLinearGradient(0, 0, 0, canvas_poster.height); gradient.addColorStop(0.5, 'rgba(0, 0, 0, 1)'); gradient.addColorStop(0.6, 'rgba(0, 0, 0, 0)'); gradient.addColorStop(1, 'rgba(0, 0, 0, 1)'); ctx_poster.globalCompositeOperation = 'destination-out'; ctx_poster.fillStyle = gradient; ctx_poster.fillRect(0, 0, canvas_poster.width, canvas_poster.height); ctx_poster.globalCompositeOperation = 'source-over'; getImg(function (blured) { canvas_poster.width = w; canvas_poster.height = h; ctx_poster.drawImage(img, -(nw - canvas_poster.width) / 2, -(nh - canvas_poster.height) / 2, nw, nh); var gradient = ctx_poster.createLinearGradient(0, 0, 0, canvas_poster.height); gradient.addColorStop(0, 'rgba(0, 0, 0, 1)'); gradient.addColorStop(0.6, 'rgba(0, 0, 0, 1)'); gradient.addColorStop(0.65, 'rgba(0, 0, 0, 0)'); gradient.addColorStop(1, 'rgba(0, 0, 0, 0)'); ctx_poster.globalCompositeOperation = 'destination-in'; ctx_poster.fillStyle = gradient; ctx_poster.fillRect(0, 0, canvas_poster.width, canvas_poster.height); ctx_poster.globalCompositeOperation = 'source-over'; ctx_poster.drawImage(blured, 0, 0); getImg(callback); }); }); }, 100); } function rgbToHex(r, g, b) { return "#" + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1); } function circlePattern() { var size = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 20; var w = 200; var h = 200; reset$2(w, h); var r = size / 2; for (var y = 0; y < h; y += size) { var offset = y / size % 2 ? r : 0; for (var x = 0; x < w; x += size) { ctx.beginPath(); ctx.arc(x + r + offset, y + r, r, 0, Math.PI * 2); ctx.fillStyle = Math.random() > 0.5 ? '#000' : '#fff'; ctx.fill(); } } return canvas.toDataURL(); } var Color = { get: get$f, extract: extract$2, palette: palette, rgba: rgba, blur: blur$1, tone: tone, rgbToHsl: rgbToHsl, rgbToHex: rgbToHex, hslToRgb: hslToRgb$1, blurPoster: blurPoster, circlePattern: circlePattern }; var lifecycleEvents = Subscribe$2(); var restoring = false; var deferred = []; function whenRestored(call) { if (restoring) deferred.push(call);else call(); } // AVPlay forbids further calls until restoreAsync has invoked a callback. function lifecycle(av, doc, onError, getUrl) { var saved; var disposed = false; var phase = 'active'; var settleTimer; var settleDeadline; var readyStarted = false; function report(type) { var details = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; lifecycleEvents.send('event', Object.assign({ type: type }, details)); } function release() { restoring = false; var calls = deferred; deferred = []; calls.forEach(function (call) { return call(); }); } function fail(type, error) { clearTimeout(settleTimer); phase = 'active'; report(type, { error: error && error.name || 'Error' }); saved = undefined; if (!disposed) onError(); } function settle() { if (disposed || phase !== 'resuming') return; try { var state = av.getState(); // On this TV the restore callback precedes completion of buffering. if (state !== 'PLAYING' && state !== 'PAUSED') { if (Date.now() >= settleDeadline) throw { name: 'RestoreTimeoutError' }; if (state === 'READY' && !readyStarted) { readyStarted = true; av.play(); } settleTimer = setTimeout(settle, 100); return; } if (saved.paused && state === 'PLAYING') av.pause();else if (!saved.paused && state === 'PAUSED') av.play(); state = av.getState(); report('restored', { state: state, positionMs: av.getCurrentTime(), beforeState: saved.state, beforePositionMs: saved.positionMs }); phase = 'active'; saved = undefined; if (doc.hidden) hide(); } catch (error) { fail('restore-error', error); } } function hide() { if (disposed || restoring || phase !== 'active') return; try { var state = av.getState(); if (state !== 'PLAYING' && state !== 'PAUSED') return; if (typeof av.suspend !== 'function' || typeof av.restoreAsync !== 'function' && typeof av.restore !== 'function') { report('suspend-unavailable'); return; } saved = { state: state, positionMs: av.getCurrentTime(), paused: state === 'PAUSED' }; try { saved.durationMs = av.getDuration(); } catch (error) { saved.durationMs = 0; } av.suspend(); phase = 'suspended'; report('suspended', { state: saved.state, positionMs: saved.positionMs }); } catch (error) { fail('suspend-error', error); } } function show() { if (disposed || restoring || phase !== 'suspended') return; phase = 'restoring'; restoring = true; report('restore-start', { state: saved.state, positionMs: saved.positionMs }); var settled = false; function success() { if (settled) return; settled = true; // This callback is the first point at which AVPlay calls are safe. if (disposed) { release(); return; } phase = 'resuming'; readyStarted = false; settleDeadline = Date.now() + 15000; try { report('restore-callback', { state: av.getState() }); } catch (error) { fail('restore-error', error); } release(); settle(); } function failure(error) { if (settled) return; settled = true; fail('restore-error', error); release(); } try { // Some TV bindings reject null optional arguments with TypeMismatchError. // Keep the URL private and pass the captured position as an unsigned integer. var url = getUrl(); if (typeof url !== 'string' || !url) throw { name: 'InvalidValuesError' }; var position = Math.max(0, Math.min(4294967295, Math.round(saved.positionMs))); if (saved.durationMs <= 0 && position === 0) position = 1; if (typeof av.restoreAsync === 'function') av.restoreAsync(url, position, false, success, failure);else { av.restore(url, position, false); success(); } } catch (error) { failure(error); } } function visibility() { if (doc.hidden) hide();else show(); } doc.addEventListener('visibilitychange', visibility); return { blocked: function blocked() { return restoring || phase === 'suspended' || phase === 'resuming'; }, position: function position() { return saved ? saved.positionMs / 1000 : 0; }, duration: function duration() { return saved ? saved.durationMs / 1000 : 0; }, paused: function paused() { return saved ? saved.paused : false; }, intent: function intent(paused) { if (saved) saved.paused = paused; }, visibility: visibility, destroy: function destroy() { disposed = true; clearTimeout(settleTimer); doc.removeEventListener('visibilitychange', visibility); saved = undefined; } }; } function AVPlay$1(call_video) { var stream_url, loaded; console.log('Player', 'run Tizen'); var object = $(''); var video = object[0]; var listener = Subscribe$2(); var destroyed = false; var lifecycle$1 = lifecycle(webapis.avplay, document, function () { video.error = { code: 'tizen', message: 'Failed to restore playback. Please reopen the video.' }; listener.send('error', { error: video.error }); }, function () { return stream_url; }); var change_scale_later; var change_speed_later; object.width(window.innerWidth); object.height(window.innerHeight); /** * Установить урл */ Object.defineProperty(video, "src", { set: function set(url) { whenRestored(function () { if (url && !destroyed) { stream_url = url; try { webapis.avplay.open(url); webapis.avplay.setDisplayRect(0, 0, window.innerWidth, window.innerHeight); webapis.avplay.setDisplayMethod('PLAYER_DISPLAY_MODE_LETTER_BOX'); } catch (e) { listener.send('error', { error: { code: 'tizen', message: 'failed to initialize player' } }); } try { webapis.avplay.setSilentSubtitle(false); } catch (e) {} } }); }, get: function get() {} }); /** * Позиция */ Object.defineProperty(video, "currentTime", { set: function set(t) { if (lifecycle$1.blocked()) return; try { webapis.avplay.seekTo(t * 1000); } catch (e) {} }, get: function get() { if (lifecycle$1.blocked()) return lifecycle$1.position(); var d = 0; try { d = webapis.avplay.getCurrentTime(); } catch (e) {} return d ? d / 1000 : 0; } }); /** * Длительность */ Object.defineProperty(video, "duration", { set: function set() {}, get: function get() { if (lifecycle$1.blocked()) return lifecycle$1.duration(); var d = 0; try { d = webapis.avplay.getDuration(); } catch (e) {} return d ? d / 1000 : 0; } }); /** * Пауза */ Object.defineProperty(video, "paused", { set: function set() {}, get: function get() { if (lifecycle$1.blocked()) return lifecycle$1.paused(); try { return webapis.avplay.getState() == 'PAUSED'; } catch (e) { return false; } } }); /** * Аудиодорожки */ Object.defineProperty(video, "audioTracks", { set: function set() {}, get: function get() { if (lifecycle$1.blocked()) return []; try { var totalTrackInfo = webapis.avplay.getTotalTrackInfo(); var tracks = totalTrackInfo.filter(function (track) { return track.type === 'AUDIO'; }).map(function (track) { var info = JSON.parse(track.extra_info); var item = { extra: JSON.parse(track.extra_info), index: parseInt(track.index), language: info.language }; Object.defineProperty(item, "enabled", { set: function set(v) { if (lifecycle$1.blocked()) return; if (v) { try { webapis.avplay.setSelectTrack('AUDIO', item.index); } catch (e) { console.log('Player', 'no change audio:', e.message); } } }, get: function get() {} }); return item; }).sort(function (a, b) { return a.index - b.index; }); return tracks; } catch (e) { return []; } } }); /** * Субтитры */ Object.defineProperty(video, "textTracks", { set: function set() {}, get: function get() { if (lifecycle$1.blocked()) return []; try { var totalTrackInfo = webapis.avplay.getTotalTrackInfo(); var tracks = totalTrackInfo.filter(function (track) { return track.type === 'TEXT'; }).map(function (track) { var info = JSON.parse(track.extra_info), item = { extra: JSON.parse(track.extra_info), index: parseInt(track.index), language: info.track_lang }; Object.defineProperty(item, "mode", { set: function set(v) { if (lifecycle$1.blocked()) return; if (v == 'showing') { try { webapis.avplay.setSelectTrack('TEXT', item.index); } catch (e) { console.log('Player', 'no change text:', e.message); } } }, get: function get() {} }); return item; }).sort(function (a, b) { return a.index - b.index; }); console.log('Tizen', 'tracks', tracks); console.log('Tizen', 'tracks index', tracks.map(function (a) { return a.index; })); return tracks; } catch (e) { return []; } } }); /** * Ширина видео */ Object.defineProperty(video, "videoWidth", { set: function set() {}, get: function get() { var info = videoInfo(); return info.Width || 0; } }); /** * Высота видео */ Object.defineProperty(video, "videoHeight", { set: function set() {}, get: function get() { var info = videoInfo(); return info.Height || 0; } }); /** * Получить информацию о видео * @returns {object} */ function videoInfo() { if (lifecycle$1.blocked()) return {}; try { var info = webapis.avplay.getCurrentStreamInfo(), json = {}; for (var i = 0; i < info.length; i++) { var detail = info[i]; if (detail.type == 'VIDEO') { json = JSON.parse(detail.extra_info); } } return json; } catch (e) { return {}; } } /** * Меняем размер видео * @param {string} scale - default|cover */ function changeScale(scale) { if (lifecycle$1.blocked()) return; try { if (scale == 'cover') { webapis.avplay.setDisplayMethod('PLAYER_DISPLAY_MODE_FULL_SCREEN'); } else { webapis.avplay.setDisplayMethod('PLAYER_DISPLAY_MODE_LETTER_BOX'); } } catch (e) { change_scale_later = scale; } } function changeSpeed(speed) { if (lifecycle$1.blocked()) return; try { webapis.avplay.setSpeed(speed); } catch (e) { change_speed_later = speed; } } /** * Всегда говорим да, мы можем играть */ video.canPlayType = function () { return true; }; /** * Вешаем кастомные события */ video.addEventListener = listener.follow.bind(listener); /** * Вешаем события от плеера тайзен */ whenRestored(function () { if (destroyed) return; webapis.avplay.setListener({ onbufferingstart: function onbufferingstart() { listener.send('progress', { percent: 0 }); listener.send('waiting'); }, onbufferingprogress: function onbufferingprogress(percent) { listener.send('progress', { percent: percent }); }, onbufferingcomplete: function onbufferingcomplete() { listener.send('progress', { percent: 0 }); listener.send('playing'); }, onstreamcompleted: function onstreamcompleted() { if (lifecycle$1.blocked()) return; webapis.avplay.stop(); listener.send('ended'); }, oncurrentplaytime: function oncurrentplaytime() { listener.send('timeupdate'); if (change_scale_later) { change_scale_later = false; changeScale(change_scale_later); } if (change_speed_later) { change_speed_later = false; changeSpeed(change_speed_later); } }, onerror: function onerror(eventType) { listener.send('error', { error: { code: 'tizen', message: eventType } }); }, onevent: function onevent(eventType, eventData) { console.log('Player', 'event type:', eventType, 'data:', eventData); }, onsubtitlechange: function onsubtitlechange(duration, text, data3, data4) { listener.send('subtitle', { text: text }); }, ondrmevent: function ondrmevent(drmEvent, drmData) {} }); }); /** * Загрузить */ video.load = function () { whenRestored(function () { if (stream_url && !destroyed) { webapis.avplay.prepareAsync(function () { if (destroyed) return; loaded = true; webapis.avplay.play(); try { webapis.avplay.setSilentSubtitle(false); } catch (e) {} listener.send('canplay'); listener.send('playing'); listener.send('loadeddata'); lifecycle$1.visibility(); }, function (e) { listener.send('error', { error: 'code [' + e.code + '] ' + e.message }); }); } }); }; /** * Играть */ video.play = function () { if (lifecycle$1.blocked()) { lifecycle$1.intent(false); return; } if (loaded) webapis.avplay.play(); }; /** * Пауза */ video.pause = function () { if (lifecycle$1.blocked()) { lifecycle$1.intent(true); return; } if (loaded) webapis.avplay.pause(); }; /** * Установить масштаб */ video.size = function (type) { changeScale(type); }; /** * Установить скорость */ video.speed = function (speed) { changeSpeed(speed); }; /** * Уничтожить */ video.destroy = function () { destroyed = true; lifecycle$1.destroy(); whenRestored(function () { try { webapis.avplay.close(); } catch (e) {} }); video.remove(); listener.destroy(); }; call_video(video); return object; } /** * Возможность управлять внутренними субтитрами на webos через пульт */ var subparams; var listener$l = function listener(e) { if (e.code == 405) getWebosmediaId(setSubtitleColor); if (e.code == 406) getWebosmediaId(setSubtitleBackgroundColor); if (e.code == 403) getWebosmediaId(setSubtitleFontSize); if (e.code == 404) getWebosmediaId(setSubtitlePosition); if (e.code == 55) getWebosmediaId(setSubtitleBackgroundOpacity); if (e.code == 57) getWebosmediaId(setSubtitleCharacterOpacity); }; Keypad.listener.follow('keydown', listener$l); function luna$1(params, call, fail) { if (call) params.onSuccess = call; params.onFailure = function (result) { console.log('WebOS', params.method + " [fail][" + result.errorCode + "] " + result.errorText); if (fail) fail(); }; webOS.service.request("luna://com.webos.media", params); } function initStorage() { if (!subparams) { subparams = Storage.get('webos_subs_params', '{}'); Arrays.extend(subparams, { color: 2, font_size: 1, bg_color: 'black', position: -1, bg_opacity: 0, char_opacity: 255 }); } } function subCallParams(mediaId, method, func_params) { var parameters = { mediaId: mediaId }; Arrays.extend(parameters, func_params); luna$1({ parameters: parameters, method: method }); Storage.set('webos_subs_params', subparams); } function getWebosmediaId(func) { var video = document.querySelector('video'); if (video && video.mediaId) { initStorage(); setTimeout(function () { subCallParams(video.mediaId, func.name, func()); }, 300); } } function setSubtitleColor() { subparams.color++; if (subparams.color == 6) subparams.color = 0; return { color: subparams.color }; } function setSubtitleBackgroundColor() { var bgcolors = ['black', 'white', 'yellow', 'red', 'green', 'blue']; var ixcolors = bgcolors.indexOf(subparams.bg_color); ixcolors++; if (ixcolors == -1) ixcolors = 0; subparams.bg_color = bgcolors[ixcolors]; return { bgColor: subparams.bg_color }; } function setSubtitleFontSize() { subparams.font_size++; if (subparams.font_size == 5) subparams.font_size = 0; return { fontSize: subparams.font_size }; } function setSubtitlePosition() { subparams.position++; if (subparams.position == 5) subparams.position = -3; return { position: subparams.position }; } function setSubtitleBackgroundOpacity() { subparams.bg_opacity += 15; if (subparams.bg_opacity > 255) subparams.bg_opacity = 0; return { bgOpacity: subparams.bg_opacity }; } function setSubtitleCharacterOpacity() { subparams.char_opacity += 15; if (subparams.char_opacity > 255) subparams.char_opacity = 45; return { charOpacity: subparams.char_opacity }; } function initialize() { var video = document.querySelector('video'); if (video && video.mediaId) { initStorage(); var methods = ['setSubtitleColor', 'setSubtitleBackgroundColor', 'setSubtitleFontSize', 'setSubtitlePosition', 'setSubtitleBackgroundOpacity', 'setSubtitleCharacterOpacity']; var parameters = { mediaId: video.mediaId, color: subparams.color, bgColor: subparams.bg_color, position: subparams.position, fontSize: subparams.font_size, bgOpacity: subparams.bg_opacity, charOpacity: subparams.char_opacity }; Arrays.extend(parameters, subparams); methods.forEach(function (method) { luna$1({ parameters: parameters, method: method }); }); } } var Subtitles$1 = { initialize: initialize }; /** * Для запросов в луну * @param {object} params * @param {function} call * @param {function} fail */ function luna(params, call, fail) { if (call) params.onSuccess = call; params.onFailure = function (result) { console.log('WebOS', params.method + " [fail][" + result.errorCode + "] " + result.errorText); if (fail) fail(); }; webOS.service.request("luna://com.webos.media", params); } function create$c(_video) { var video = _video; var media_id; var subtitle_visible = false; var timer; var timer_repet; var count = 0; var count_message = 0; var data = { subs: [], tracks: [] }; this.listener = Subscribe$2(); this.subscribed = false; this.repeted = false; /** * Начинаем поиск видео */ this.start = function () { timer = setInterval(this.search.bind(this), 300); }; /** * Включить/выключить сабы * @param {boolean} status */ this.toggleSubtitles = function (status) { subtitle_visible = status; luna({ method: 'setSubtitleEnable', parameters: { 'mediaId': media_id, 'enable': status } }); if (status) Subtitles$1.initialize(); }; /** * Получили сабы, выводим в панель * @param {object} info */ this.subtitles = function (info) { var _this = this; if (info.numSubtitleTracks) { var all = []; var add = function add(sub, index) { sub.index = index; sub.language = sub.language == '(null)' ? '' : sub.language; Object.defineProperty(sub, 'mode', { set: function set(v) { if (v == 'showing') { _this.toggleSubtitles(sub.index == -1 ? false : true); console.log('WebOS', 'change subtitles for id: ', media_id, ' index:', sub.index); if (sub.index !== -1) { setTimeout(function () { luna({ method: 'selectTrack', parameters: { 'type': 'text', 'mediaId': media_id, 'index': sub.index } }); }, 500); } } }, get: function get() {} }); all.push(sub); }; add({ title: Lang.translate('player_disabled'), selected: true }, -1); for (var i = 0; i < info.subtitleTrackInfo.length; i++) { add(info.subtitleTrackInfo[i], i); } data.subs = all; this.listener.send('webos_subs', { subs: data.subs }); // Video.listener.send('webos_subs',{subs:data.subs}) // Panel.setSubs(data.subs) } }; /** * Получили дорожки, выводим в панель * @param {object} info */ this.tracks = function (info) { if (info.numAudioTracks) { var all = []; var add = function add(track, index) { track.index = index; track.selected = index == -1; track.extra = { channels: track.channels, fourCC: track.codec }; Object.defineProperty(track, 'enabled', { set: function set(v) { if (v) { console.log('WebOS', 'change audio for id:', media_id, ' index:', track.index); luna({ method: 'selectTrack', parameters: { 'type': 'audio', 'mediaId': media_id, 'index': track.index } }); if (video.audioTracks) { for (var i = 0; i < video.audioTracks.length; i++) { video.audioTracks[i].enabled = false; } if (video.audioTracks[track.index]) { video.audioTracks[track.index].enabled = true; console.log('WebOS', 'change audio two method:', track.index); } } } }, get: function get() {} }); all.push(track); }; for (var i = 0; i < info.audioTrackInfo.length; i++) { add(info.audioTrackInfo[i], i); } data.tracks = all; this.listener.send('webos_tracks', { tracks: data.tracks }); // Video.listener.send('webos_tracks',{tracks:data.tracks}) // Panel.setTracks(data.tracks, true) } }; /** * Подписываемся на видео и ждем события */ this.subscribe = function () { var _this2 = this; this.subscribed = true; luna({ method: 'subscribe', parameters: { 'mediaId': media_id, 'subscribe': true } }, function (result) { if (result.sourceInfo && !_this2.sourceInfo) { _this2.sourceInfo = true; var info = result.sourceInfo.programInfo[0]; _this2.subtitles(info); _this2.tracks(info); _this2.unsubscribe(); _this2.call(); } if (result.bufferRange) { count_message++; if (count_message == 30) { _this2.unsubscribe(); _this2.call(); } } }, function () { _this2.call(); }); }; /** * Отписка от видео */ this.unsubscribe = function () { luna({ method: 'unload', parameters: { 'mediaId': media_id } }); }; /** * Сканируем наличия видео */ this.search = function () { var _this3 = this; count++; if (count > 3) { clearInterval(timer); clearInterval(timer_repet); } var rootSubscribe = function rootSubscribe() { console.log('WebOS', 'Run root', 'version:', webOS.sdk_version); _this3.toggleSubtitles(false); if (_this3.subscribed) clearInterval(timer_repet); if (!_this3.subscribed) _this3.subscribe();else { if (data.tracks.length) { _this3.listener.send('webos_tracks', { tracks: data.tracks }); // Video.listener.send('webos_tracks',{tracks:data.tracks}) // Panel.setTracks(data.tracks,true) } if (data.subs.length) { _this3.listener.send('webos_subs', { subs: data.subs }); // Video.listener.send('webos_subs',{subs:data.subs}) // Panel.setSubs(data.subs) } } clearInterval(timer); }; console.log('WebOS', 'try get id:', video.mediaId); if (video.mediaId) { media_id = video.mediaId; console.log('WebOS', 'video id:', media_id); rootSubscribe(); } }; /** * Вызываем и завершаем работу */ this.call = function () { if (this.callback) this.callback(); this.callback = false; }; this.speed = function (v) { luna({ method: 'setPlayRate', parameters: { 'mediaId': media_id, 'playRate': v, 'audioOutput': true } }); }; /** * Создаем новое видео * @param {object} new_video */ this.repet = function (new_video) { video = new_video; console.log('WebOS', 'repeat to new video', new_video ? true : false); media_id = ''; clearInterval(timer); count = 0; this.repeted = true; timer_repet = setInterval(this.search.bind(this), 300); }; /** * После перемотки включаем состояние сабов */ this.rewinded = function () { this.toggleSubtitles(subtitle_visible); }; /** * Уничтожить */ this.destroy = function () { clearInterval(timer); clearInterval(timer_repet); if (media_id) this.unsubscribe(); data = null; this.subscribed = false; this.callback = false; }; } var keydown_time = 0; var duble_click_time = 0; var move_time = 0; var touch = false; function init$1m() { Keypad.listener.follow('keydown', function () { keydown_time = Date.now(); move_time = 0; }); $(window).on('mousemove', function (e) { move_time = Date.now(); }).on('touchstart', function () { touch = true; }); Storage.listener.follow('change', function (e) { if (e.name == 'navigation_type') { Storage.set('is_true_mobile', Boolean(e.value == 'touch'), true); Controller.toContent(); window.location.reload(); } }); } function canClick(e) { return Date.now() - keydown_time < 200 ? false : touch || Platform.is('browser') || Platform.tv() || Platform.desktop() || Date.now() - move_time < 200; } function noDubleClick(e) { if (Date.now() - duble_click_time < 200) { e.preventDefault(); e.stopPropagation(); return false; } duble_click_time = Date.now(); return true; } var DeviceInput = { init: init$1m, canClick: canClick, noDubleClick: noDubleClick }; var html$o; var scroll$4; var active$8; var listener$k = Subscribe$2(); /** * Инициализирует селектбокс * @returns {void} */ function init$1l() { html$o = Template.get('selectbox'); scroll$4 = new Scroll({ mask: true, over: true }); html$o.find('.selectbox__body').append(scroll$4.render()); html$o.find('.selectbox__layer').on('click', function (e) { if (DeviceInput.canClick(e.originalEvent)) Controller.back(); }); scroll$4.addSwipeDown(function () { html$o.addClass('animate-down'); setTimeout(function () { Controller.back(); }, 200); }); $('body').append(html$o); } /** * Заполняет селектбокс элементами * @returns {void} */ function bind$4() { scroll$4.clear(); scroll$4.reset(); html$o.find('.selectbox__title').text(active$8.title); html$o.toggleClass('selectbox--fullsize', active$8.fullsize ? true : false); var need_first_scroll = true; active$8.items.forEach(function (element) { if (element.hide) return; element.title = Utils$1.capitalizeFirstLetter(element.title || ''); if (element.separator) { var _item = $('
    ' + element.title + '
    '); return scroll$4.append(_item); } var item = element.html || Template.get(element.template || (element.thumbnail ? 'selectbox_icon' : 'selectbox_item'), element); if (!element.subtitle) item.find('.selectbox-item__subtitle').remove(); if (element.thumbnail) { item.find('.selectbox-item__icon').empty().append(''); } if (element.checkbox) { item.addClass('selectbox-item--checkbox'); item.append('
    '); if (element.checked) item.addClass('selectbox-item--checked'); } if (element.ghost) item.css('opacity', 0.5); item.on('hover:focus', function (e) { // Первый скролл делаем мгновенным, чтобы не было анимации при открытии if (need_first_scroll) scroll$4.immediate($(e.target), true);else scroll$4.update($(e.target), true); need_first_scroll = false; if (active$8.onFocus) active$8.onFocus(element, e.target); }); if (!element.noenter) { var goclose = function goclose() { if (!active$8.nohide) hide$4();else { scroll$4.render().find('.selected').removeClass('selected'); item.addClass('selected'); } if (element.onSelect) element.onSelect(element, item);else if (active$8.onSelect) active$8.onSelect(element, item); }; item.on('hover:enter', function () { if (element.checkbox) { element.checked = !element.checked; item.toggleClass('selectbox-item--checked', element.checked); if (element.onCheck) element.onCheck(element, item);else if (active$8.onCheck) active$8.onCheck(element, item); } else if (active$8.onBeforeClose) { if (active$8.onBeforeClose()) goclose(); } else goclose(); }).on('hover:long', function (e) { if (active$8.onLong) active$8.onLong(element, e.target); }); } if (element.selected) item.addClass('selected'); if (element.picked) item.addClass('picked'); if (active$8.nomark) item.addClass('nomark'); if (element.onDraw) element.onDraw(item, element);else if (active$8.onDraw) active$8.onDraw(item, element); scroll$4.append(item); }); if (active$8.onFullDraw) active$8.onFullDraw(scroll$4); } /** * Отображает селектбокс * @param {object} object - параметры селектбокса * @param {string} object.title - заголовок селектбокса * @param {boolean} [object.fullsize=false] - использовать весь экран * @param {Array} object.items - массив элементов * @param {boolean} [object.nomark=false] - не выделять выбранный элемент * @param {boolean} [object.nohide=false] - не закрывать селектбокс после выбора элемента * @param {function} [object.onSelect] - вызывается при выборе элемента * @param {function} [object.onCheck] - вызывается при изменении состояния чекбокса * @param {function} [object.onFocus] - вызывается при фокусе на элементе * @param {function} [object.onLong] - вызывается при долгом нажатии на элемент * @param {function} [object.onBack] - вызывается при закрытии селектбокса * @param {function} [object.onDraw] - вызывается при отрисовке элемента * @param {function} [object.onFullDraw] - вызывается после полной отрисовки всех элементов * @param {function} [object.onBeforeClose] - вызывается перед закрытием селектбокса, если возвращает true, селектбокс закроется * @param {boolean} [object.noenter=false] - отключить выбор элемента по кнопке ОК/Enter * @returns {void} */ function show$e(object) { active$8 = object; scroll$4.body().toggleClass('notransition', true); listener$k.send('preshow', { active: active$8 }); bind$4(); $('body').toggleClass('selectbox--open', true); html$o.find('.selectbox__body').addClass('layer--wheight').css('max-height', window.innerWidth <= 480 ? window.innerHeight * 0.6 : 'unset').data('mheight', html$o.find('.selectbox__head')); html$o.addClass('animate'); Activity.mixState('select=open'); listener$k.send('fullshow', { active: active$8, html: html$o }); toggle$c(); } /** * Переключает контроллер на селектбокс * @returns {void} */ function toggle$c() { Controller.add('select', { toggle: function toggle() { var selected = scroll$4.render().find('.selected'); Controller.collectionSet(html$o); Controller.collectionFocus(selected.length ? selected[0] : false, html$o); listener$k.send('toggle', { active: active$8, html: html$o }); }, up: function up() { Navigator.move('up'); }, down: function down() { Navigator.move('down'); }, left: close$a, back: close$a }); Controller.toggle('select'); } /** * Скрывает селектбокс * @returns {void} */ function hide$4() { $('body').toggleClass('selectbox--open', false); html$o.removeClass('animate animate-down'); listener$k.send('hide', { active: active$8 }); } /** * Проверяет, открыт ли селектбокс * @returns {boolean} - true, если селектбокс открыт, иначе false */ function opened$3() { return $('body').hasClass('selectbox--open'); } /** * Закрывает селектбокс * @returns {void} */ function close$a() { hide$4(); Activity.mixState(); if (active$8.onBack) active$8.onBack(); listener$k.send('close', { active: active$8 }); } /** * Возвращает HTML селектбокса * @param {boolean} [js=false] - вернуть DOM-элемент вместо jQuery * @returns {jQuery|HTMLElement} - HTML селектбокса */ function render$m(js) { return js ? html$o[0] : html$o; } var Select = { listener: listener$k, init: init$1l, show: show$e, opened: opened$3, hide: hide$4, close: close$a, render: render$m }; var elems$1; var html$n; function init$1k() { html$n = Template.get('player_panel'); elems$1 = { peding: $('.player-panel__peding', html$n), position: $('.player-panel__position', html$n), time: $('.player-panel__time', html$n), timenow: $('.player-panel__timenow', html$n), timeend: $('.player-panel__timeend', html$n), title: $('.player-panel__filename', html$n), tracks: $('.player-panel__tracks', html$n), subs: $('.player-panel__subs', html$n), timeline: $('.player-panel__timeline', html$n), quality: $('.player-panel__quality', html$n), flow: $('.player-panel__flow', html$n), episode: $('.player-panel__next-episode-name', html$n), rewind_touch: $('.player-panel__time-touch-zone', html$n), playlist: html$n.find('.player-panel__playlist'), next: html$n.find('.player-panel__next'), prev: html$n.find('.player-panel__prev'), playpause: html$n.find('.player-panel__playpause'), rprev: html$n.find('.player-panel__rprev'), rnext: html$n.find('.player-panel__rnext'), tstart: html$n.find('.player-panel__tstart'), tend: html$n.find('.player-panel__tend'), fullscreen: html$n.find('.player-panel__fullscreen'), settings: html$n.find('.player-panel__settings'), pip: html$n.find('.player-panel__pip'), volume: html$n.find('.player-panel__volume'), left: html$n.find('.player-panel__left'), right: html$n.find('.player-panel__right'), center: html$n.find('.player-panel__center'), playlist_buttons: html$n.find('.player-panel__playlist-buttons'), mobile_more: $('.player-panel__mobile-more', html$n), iptv_channel: $('.player-panel-iptv__channel', html$n), iptv_arrow_up: $('.player-panel-iptv__arrow-up', html$n), iptv_arrow_down: $('.player-panel-iptv__arrow-down', html$n), iptv_position: $('.player-panel-iptv__position', html$n), segments: $('.player-panel__timeline-segments', html$n) }; } function elem(name) { return elems$1[name]; } function render$l() { return html$n; } var Html = { init: init$1k, render: render$l, elem: elem }; var listener$j = Subscribe$2(); var tracks = []; var subs = []; var flows = false; var qualitys = false; var translates = {}; function normalName(name) { return name.replace(/^[0-9]+(\.)?([\t ]+)?/, '').replace(/\s#[0-9]+/, ''); } function init$1j() { /** * Выбор потока */ Html.elem('flow').on('hover:enter', function () { if (flows) { var enabled = Controller.enabled().name; Select.show({ title: Lang.translate('player_flow'), items: flows, onSelect: function onSelect(a) { flows.forEach(function (element) { element.enabled = false; element.selected = false; }); a.enabled = true; a.selected = true; Controller.toggle(enabled); listener$j.send('flow', { url: a.url }); }, onBack: function onBack() { Controller.toggle(enabled); } }); } }); /** * Выбор качества */ Html.elem('quality').text('auto').on('hover:enter', function () { if (qualitys) { var qs = []; var nw = Html.elem('quality').text(); if (Arrays.isArray(qualitys)) { qs = qualitys; } else { for (var i in qualitys) { var qa = qualitys[i]; var qu = _typeof(qa) == 'object' ? qa.url : typeof qa == 'string' ? qa : ''; var lb = _typeof(qa) == 'object' ? qa.label : ''; qs.push({ quality: i, title: i + (lb ? '' + lb + '' : ''), url: qu, selected: nw == Utils$1.qualityToText(i), call: _typeof(qa) == 'object' ? qa.call : false, instance: qa }); } } if (!qs.length) return; var enabled = Controller.enabled().name; Select.show({ title: Lang.translate('player_quality'), items: qs, onSelect: function onSelect(a) { if (a.call) { Controller.toggle(enabled); a.call(a.instance, function (url) { Html.elem('quality').text(Utils$1.qualityToText(a.quality)); qs.forEach(function (q) { return q.selected = false; }); a.selected = true; listener$j.send('quality', { name: a.quality, url: url }); if (a.instance && a.instance.trigger) a.instance.trigger(); }); } else { Html.elem('quality').text(Utils$1.qualityToText(a.quality)); qs.forEach(function (q) { return q.selected = false; }); a.enabled = true; a.selected = true; if (!Arrays.isArray(qualitys) || a.change_quality) listener$j.send('quality', { name: a.quality, url: a.url }); if (a.instance && a.instance.trigger) a.instance.trigger(); Controller.toggle(enabled); } }, onBack: function onBack() { Controller.toggle(enabled); } }); } }); /** * Выбор аудиодорожки */ Html.elem('tracks').on('hover:enter', function (e) { if (tracks.length) { tracks.forEach(function (element, p) { var name = []; var from = translates.tracks && Arrays.isArray(translates.tracks) && translates.tracks[p] ? translates.tracks[p] : element; name.push(p + 1); name.push(normalName(from.language || from.name || Lang.translate('player_unknown'))); if (from.label) name.push(normalName(from.label)); if (from.extra) { if (from.extra.channels) name.push(from.extra.channels + ' Ch'); if (from.extra.fourCC) name.push(from.extra.fourCC); } element.title = name.join(' / '); }); var enabled = Controller.enabled().name; Select.show({ title: Lang.translate('player_tracks'), items: tracks, onSelect: function onSelect(a) { tracks.forEach(function (element) { element.enabled = false; element.selected = false; }); a.enabled = true; a.selected = true; Controller.toggle(enabled); if (a.onSelect) a.onSelect(a); }, onBack: function onBack() { Controller.toggle(enabled); } }); } }); /** * Выбор субтитров */ Html.elem('subs').on('hover:enter', function (e) { if (subs.length) { if (subs[0].index !== -1) { var any_select = subs.find(function (s) { return s.selected; }); Arrays.insert(subs, 0, { title: Lang.translate('player_disabled'), selected: any_select ? false : true, index: -1 }); } subs.forEach(function (element, p) { if (element.index !== -1) { var track_num = element.extra && element.extra.track_num ? parseInt(element.extra.track_num) : element.index; var from = translates.subs && Arrays.isArray(translates.subs) && translates.subs[track_num] ? translates.subs[track_num] : element; element.title = p + ' / ' + normalName(from.language && from.label ? from.language + ' / ' + from.label : from.language || from.label || Lang.translate('player_unknown')); } }); var enabled = Controller.enabled().name; Select.show({ title: Lang.translate('player_subs'), items: subs, onSelect: function onSelect(a) { subs.forEach(function (element) { element.mode = 'disabled'; element.selected = false; }); a.mode = 'showing'; a.selected = true; listener$j.send('subsview', { status: a.index > -1 }); Controller.toggle(enabled); if (a.onSelect) a.onSelect(a); }, onBack: function onBack() { Controller.toggle(enabled); } }); } }); Html.elem('mobile_more').on('hover:enter', function (e) { var enabled = Controller.enabled().name; var items = []; items.push({ title: Lang.translate('player_quality'), template: 'selectbox_icon', icon: '', onSelect: function onSelect() { Controller.toggle(enabled); Html.elem('quality').trigger('hover:enter'); } }); Html.render().find('.player-panel__tv-visible .button:not(.hide, .player-panel__fullscreen)').each(function (i, el) { var btn = $(el); var icon = btn.find('svg').clone().wrap('
    ').parent().html(); var title = btn.find('.tooltip').text(); if (!title) return; items.push({ title: title, template: 'selectbox_icon', icon: icon, onSelect: function onSelect() { Controller.toggle(enabled); btn.trigger('hover:enter'); } }); }); Select.show({ title: Lang.translate('more'), items: items, onBack: function onBack() { Controller.toggle(enabled); } }); }); } /** * Установить субтитры * @param {Array} su - массив субтитров */ function setSubtitles(su) { if (!su) return; subs = su; Html.elem('subs').toggleClass('hide', false); } /** * Установить аудиодорожки * @param {Array} tr - массив аудиодорожек */ function setTracks(tr) { if (!tr) return; tracks = tr; Html.elem('tracks').toggleClass('hide', false); } /** * Установить уровни качества из HLS * @param {Object} levels * @param {String} current */ function setLevels(levels, current) { if (qualitys && Object.keys(qualitys).length) return; qualitys = levels; Html.elem('quality').text(Utils$1.qualityToText(current)); } /** * Установить выбор качества * @param {Object|Array} qs * @param {String} url */ function setQualitys(qs, url) { if (qs) { Html.elem('quality').toggleClass('hide', false); qualitys = qs; for (var i in qs) { var qa = qs[i]; var qu = _typeof(qa) == 'object' ? qa.url : typeof qa == 'string' ? qa : ''; if (qu == url) { Html.elem('quality').text(Utils$1.qualityToText(i)); break; } } } } /** * Установить переводы дорожек и субтитров * @param {Object} data - объект переводов */ function setTranslates(data) { if (_typeof(data) == 'object') translates = data; } /** * Обновить переводы дорожек и субтитров * @param {String} where - tracks/subs * @param {Array} data - массив переводов */ function updateTranslate(where, data) { if (!translates[where]) translates[where] = data; } /** * Установить потоки * @param {Object} data - объект потоков */ function setFlows(data) { flows = _typeof(data) == 'object' ? data : false; Html.elem('flow').toggleClass('hide', flows ? false : true); } /** * Проверить наличие качества * @returns {Boolean|Object|Array} - false если нет качества, иначе объект или массив качества */ function hasQuality() { return qualitys; } function destroy$q() { tracks = []; subs = []; qualitys = false; flows = false; translates = {}; Html.elem('quality').text('auto'); Html.elem('subs').toggleClass('hide', true); Html.elem('tracks').toggleClass('hide', true); Html.elem('flow').toggleClass('hide', true); } var Option = { init: init$1j, setSubtitles: setSubtitles, setTracks: setTracks, setLevels: setLevels, setQualitys: setQualitys, setTranslates: setTranslates, updateTranslate: updateTranslate, setFlows: setFlows, hasQuality: hasQuality, destroy: destroy$q }; var language_codes = { ara: 'ar', arm: 'hy', hye: 'hy', aze: 'az', bel: 'be', bul: 'bg', chi: 'zh', zho: 'zh', cze: 'cs', ces: 'cs', dan: 'da', dut: 'nl', nld: 'nl', eng: 'en', est: 'et', fin: 'fi', fre: 'fr', fra: 'fr', geo: 'ka', kat: 'ka', ger: 'de', deu: 'de', gre: 'el', ell: 'el', heb: 'he', hin: 'hi', hun: 'hu', ice: 'is', isl: 'is', ita: 'it', jpn: 'ja', kaz: 'kk', kor: 'ko', lav: 'lv', lit: 'lt', mac: 'mk', mkd: 'mk', nor: 'no', per: 'fa', fas: 'fa', pol: 'pl', por: 'pt', rum: 'ro', ron: 'ro', rus: 'ru', slo: 'sk', slk: 'sk', slv: 'sl', spa: 'es', swe: 'sv', tur: 'tr', ukr: 'uk', uzb: 'uz', vie: 'vi' }; function languageCode$1(value) { var code = String(value || '').trim().toLowerCase().replace(/_/g, '-').split('-')[0]; if (code == 'ua') code = 'uk'; return language_codes[code] || code; } /** * Выбрать субтитры для автозапуска: сначала дорожки нужного языка, среди них «Полные», иначе первая * @param {array} subs - список субтитров * @param {string} code - код языка (ru, en, es, spa, es-ES...) * @returns {object|undefined} */ function preferredSubtitle(subs, code) { var list = subs.filter(function (s) { return s; }); var lang = languageCode$1(code); var same = list.filter(function (s) { return languageCode$1(s.language || s.lang || s.srclang) == lang; }); var pool = same.length ? same : list; var full = function full(s) { return (s.label || '').indexOf('олные') >= 0; }; return lang == 'ru' && list.find(full) || pool.find(full) || pool[0]; } function usefulDescription(tags) { var title = tags && (tags.title || tags.TITLE); var handler = tags && (tags.handler_name || tags.HANDLER_NAME); var description = String(title || handler || '').trim(); return /^(sound|audio)\s*handler$/i.test(description) ? '' : description; } function tracksFromFfprobe(streams) { if (!Array.isArray(streams)) return []; return streams.filter(function (stream) { return stream && stream.codec_type == 'audio'; }).map(function (stream) { var tags = stream.tags || {}; return { language: tags.language || tags.LANGUAGE || '', label: usefulDescription(tags), extra: { channels: stream.channel_layout || stream.channels, fourCC: stream.codec_name || stream.codec_long_name || '' } }; }); } var _webos = null; function convertToArray$1(arr) { if (!Arrays.isArray(arr)) { var new_arr = []; for (var index = 0; index < arr.length; index++) { new_arr.push(arr[index]); } arr = new_arr; } return arr; } function loadSubs(params, subsview) { if (!_webos.parsed_subtitles) return; var subs = _webos.parsed_subtitles; PlayerVideo.video().webos_subs = subs; var inx = params.sub + 1; if (typeof params.sub !== 'undefined' && subs[inx]) { subs.forEach(function (e) { e.mode = 'disabled'; e.selected = false; }); subs[inx].mode = 'showing'; subs[inx].selected = true; console.log('WebOS', 'enable subs', inx); subsview(true); } else if (Storage.field('subtitles_start')) { var code = Storage.field('subtitles_lang'); var pick = preferredSubtitle(subs.slice(1), code == 'auto' ? Storage.get('language', 'ru') : code); subs[0].selected = false; if (pick) { pick.mode = 'showing'; pick.selected = true; } subsview(true); } } function loadTracks(params) { if (!_webos.parsed_tracks) return; var tracks = _webos.parsed_tracks; PlayerVideo.video().webos_tracks = tracks; if (typeof params.track !== 'undefined' && tracks[params.track]) { tracks.forEach(function (e) { return e.selected = false; }); console.log('WebOS', 'enable tracks', params.track); tracks[params.track].enabled = true; tracks[params.track].selected = true; } } /** * Инициализация WebOS */ function setup() { if (!Platform.is('webos') || _webos || Player.playdata().voiceovers) return; _webos = new create$c(PlayerVideo.video()); _webos.callback = function () { var currentVideo = PlayerVideo.video(); var src = currentVideo.src; var sub = currentVideo.customSubs; console.log('WebOS', 'video loaded'); $(currentVideo).remove(); var norm = PlayerVideo.normalization(); if (norm) norm.destroy(); PlayerVideo.url(src, true); var newVideo = PlayerVideo.video(); newVideo.customSubs = sub; _webos.repet(newVideo); PlayerVideo.listener.send('reset_continue', {}); }; _webos.listener.follow('webos_subs', function (e) { PlayerVideo.listener.send('webos_subs', e); // Для совместимости с плагинами, которые используют старый способ получения субтитров _webos.parsed_subtitles = convertToArray$1(e.subs); Option.setSubtitles(e.subs); }); _webos.listener.follow('webos_tracks', function (e) { PlayerVideo.listener.send('webos_tracks', e); // Для совместимости с плагинами, которые используют старый способ получения аудиодорожек _webos.parsed_tracks = convertToArray$1(e.tracks); Option.setTracks(e.tracks); }); _webos.start(); } function handleLoaded(params, subsview) { if (!_webos || !_webos.sourceInfo) return false; if (_webos.parsed_tracks) loadTracks(params); if (_webos.parsed_subtitles) loadSubs(params, subsview); return true; } function audioTracks$2() { if (_webos && _webos.sourceInfo) return PlayerVideo.video().webos_tracks || []; return null; } function rewinded() { if (_webos) _webos.rewinded(); } function speed$1(value) { if (_webos) _webos.speed(value); } function isActive$3() { return !!_webos; } function destroy$p() { if (_webos) _webos.destroy(); _webos = null; } var WebOSManager = { setup: setup, handleLoaded: handleLoaded, audioTracks: audioTracks$2, rewinded: rewinded, speed: speed$1, isActive: isActive$3, destroy: destroy$p }; var video_tube = []; function register(params) { if (typeof params.verify === 'function' && typeof params.create === 'function') { if (video_tube.indexOf(params) == -1) video_tube.push(params); return true; } return false; } function verify(src) { var find = video_tube.find(function (e) { return e.verify(src); }); return find ? find : false; } function remove$5(params) { Arrays.remove(video_tube, params); } var Tube = { register: register, verify: verify, remove: remove$5 }; function parse$c(data) { var result = { hash_string: '', season: null, episode: null, serial: !!data.movie.number_of_seasons }; var regexps = [[/\bs(\d+)\.?ep?(\d+)\b/i, 'season', 'episode'], [/\b(\d{1,2})[x\-](\d+)\b/i, 'season', 'episode'], [/\bs(\d{2})(\d{2,3})\b/i, 'season', 'episode'], [/season (\d+) episode (\d+)/i, 'season', 'episode'], [/сезон (\d+) серия (\d+)/i, 'season', 'episode'], [/(\d+) season (\d+) episode/i, 'season', 'episode'], [/(\d+) сезон (\d+) серия/i, 'season', 'episode'], [/episode (\d+)/i, 'episode'], [/серия (\d+)/i, 'episode'], [/(\d+) episode/i, 'episode'], [/(\d+) серия/i, 'episode'], [/season (\d+)/i, 'season'], [/сезон (\d+)/i, 'season'], [/(\d+) season/i, 'season'], [/(\d+) сезон/i, 'season'], [/\bs(\d+)\b/i, 'season'], [/\bep?\.?(\d+)\b/i, 'episode'], [/\b(\d{1,3}) of (\d+)/i, 'episode'], [/\b(\d{1,3}) из (\d+)/i, 'episode'], [/ - (\d{1,3})\b/i, 'episode'], [/\[(\d{1,3})\]/i, 'episode'], [/(\d+) сер/i, 'episode']]; var folder_regexps = [[/season (\d+)/i, 'season'], [/сезон (\d+)/i, 'season'], [/(\d+) season/i, 'season'], [/(\d+) сезон/i, 'season'], [/\bs(\d+)\b/i, 'season']]; var parts = data.path.replace(/_/g, ' ').split('/'); var fname = parts.pop(); var folder = parts.pop(); regexps.forEach(function (regexp) { var match = fname.match(regexp[0]); if (match) { var arr = regexp.slice(1); arr.forEach(function (a, i) { var v = match[i + 1]; if (v && result[a] == null) result[a] = parseInt(v); }); } }); if (folder && result.season == null) { folder_regexps.forEach(function (regexp) { var match = folder.match(regexp[0]); if (match) { var arr = regexp.slice(1); arr.forEach(function (a, i) { var v = match[i + 1]; if (v && result[a] == null) result[a] = parseInt(v); }); } }); } if (result.season == null) result.season = data.movie.number_of_seasons ? 1 : 0; if (result.episode == null) { var match = data.filename.replace(/_/g, ' ').trim().match(/^(\d{1,3})\b/i); result.episode = match ? parseInt(match[1]) : 0; } if (!data.is_file) { if (data.movie.number_of_seasons) { result.hash_string = [result.season, result.season > 10 ? ':' : '', result.episode, data.movie.original_title].join(''); } else if (data.movie.original_title && !result.serial) { result.hash_string = data.movie.original_title; } else { result.hash_string = data.path; } } else { result.hash_string = data.path; } return result; } var EpisodeParser = { parse: parse$c }; var network$b = new Request(); var gst_work = false; /** * Возвращает url сервера * @returns {string} */ function url$5() { var u = ip(); return u ? Utils$1.checkEmptyUrl(u) : u; } /** * Возвращает ip сервера * @returns {string} */ function ip() { var one = Storage.get('torrserver_url'); var two = Storage.get('torrserver_url_two'); return Storage.field('torrserver_use_link') == 'two' ? two || one : one || two; } /** * Возвращает список торрентов * @param {function} success - функция при успешном получении списка * @param {function} fail - функция при ошибке получения списка * @returns {void} */ function my(success, fail) { var data = JSON.stringify({ action: 'list' }); clear$d(); network$b.silent(url$5() + '/torrents', function (result) { if (result.length) success(result);else fail(); }, fail, data); } /** * Возвращает статистику загрузки торрента по хэшу * @param {string} hash - хэш торрента * @param {function} success - функция при успешном получении статистики */ function cache$1(hash, success, fail) { var data = JSON.stringify({ action: 'get', hash: hash }); network$b.silent(url$5() + '/cache', success, fail, data); } function viewed$2(hash, success, fail) { var data = JSON.stringify({ action: 'list', hash: hash }); clear$d(); network$b.timeout(5000); network$b.silent(url$5() + '/viewed', success, fail, data); } function viewedSet(hash, file_index, timecode, success, fail) { var data = JSON.stringify({ action: 'set', hash: hash, file_index: file_index, timecode: timecode }); network$b.silent(url$5() + '/viewed', success, fail, data); } /** * Добавляет торрент на сервер * @param {object} object - объект с данными торрента * @param {function} success - функция при успешном добавлении * @param {function} fail - функция при ошибке добавления */ function add$e(object, success, fail) { var send_data = object.data ? Arrays.clone(object.data) : false; var category = 'movie'; if (send_data && send_data.movie) { category = send_data.movie.name ? 'tv' : 'movie'; send_data.movie = Utils$1.clearCard(send_data.movie); } var json = { action: 'add', link: object.link, title: '[LAMPA] ' + (object.title + '').replace('??', '?'), poster: object.poster, data: send_data ? JSON.stringify(send_data) : '', save_to_db: true, category: category }; var data = JSON.stringify(json); clear$d(); network$b.silent(url$5() + '/torrents', success, fail, data); } /** * Добавляет торрент на сервер с проверкой хэша * @param {object} object - объект с данными торрента * @param {function} success - функция при успешном добавлении * @param {function} fail - функция при ошибке добавления */ function hash$1(object, success, fail) { var send_data = object.data ? Arrays.clone(object.data) : false; var category = 'movie'; if (send_data && send_data.movie) { category = send_data.movie.name ? 'tv' : 'movie'; send_data.movie = Utils$1.clearCard(send_data.movie); } var json = { action: 'add', link: object.link, title: '[LAMPA] ' + (object.title + '').replace('??', '?'), poster: object.poster, data: send_data ? JSON.stringify(send_data) : '', save_to_db: Storage.get('torrserver_savedb', 'false'), category: category }; var data = JSON.stringify(json); clear$d(); network$b.silent(url$5() + '/torrents', success, function (a, c) { fail(network$b.errorDecode(a, c)); }, data); } /** * Возвращает список файлов торрента по хэшу * @param {string} hash - хэш торрента * @param {function} success - функция при успешном получении списка файлов * @param {function} fail - функция при ошибке получения списка файлов */ function files$1(hash, success, fail) { var data = JSON.stringify({ action: 'get', hash: hash }); clear$d(); network$b.timeout(2000); network$b.silent(url$5() + '/torrents', function (json) { if (json.file_stats) { success(json); } }, fail, data); } /** * Проверяет соединение с сервером * @param {function} success - функция при успешном соединении * @param {function} fail - функция при ошибке соединения */ function connected(success, fail) { clear$d(); network$b.timeout(5000); network$b.silent(url$5() + '/settings', function (json) { if (typeof json.CacheSize == 'undefined') { fail(Lang.translate('torrent_error_nomatrix')); } else { success(json); } gstCheck(); }, function (a, c) { fail(network$b.errorDecode(a, c)); }, JSON.stringify({ action: 'get' })); } /** * Проверяет работу gstreamer на сервере * @returns {void} */ function gstCheck() { network$b.silent(url$5() + '/gst/echo', function (json) { gst_work = true; }, function (a, c) { gst_work = false; }); } /** * Возвращает true, если gstreamer работает на сервере * @returns {boolean} */ function gstWork() { return Storage.field('torrserver_gts') && gst_work; } /** * Возвращает ссылку на потоковое воспроизведение файла торрента * @param {string} path - путь к файлу торрента * @param {string} hash - хэш торрента * @param {number} id - индекс файла в торрента * @returns {string} - ссылка на потоковое воспроизведение */ function stream(path, hash, id) { if (gstWork()) return url$5() + '/gst/' + encodeURIComponent(hash) + '/master.m3u8?index=' + id + '&audio=0'; return url$5() + '/stream/' + encodeURIComponent(path.split('\\').pop().split('/').pop()) + '?link=' + hash + '&index=' + id + '&' + (Storage.field('torrserver_preload') ? 'preload' : 'play'); } /** * Возвращает ссылку на потоковое воспроизведение файла торрента с заменой параметра preload на play * @param {string} url - ссылка на потоковое воспроизведение * @returns {string} - ссылка на потоковое воспроизведение с заменой параметра preload на play */ function toPlayUrl(url) { return ip() && url.indexOf(ip()) >= 0 ? url.replace('&preload', '&play') : url; } /** * Дропает торрент с сервера * @param {string} hash - хэш торрента * @param {function} success - функция при успешном удалении * @param {function} fail - функция при ошибке удаления */ function drop(hash, success, fail) { if (gstWork()) return network$b.silent(url$5() + '/gst/remove?hash=' + encodeURIComponent(hash), success, fail, data, { dataType: 'text' }); var data = JSON.stringify({ action: 'drop', hash: hash }); clear$d(); network$b.silent(url$5() + '/torrents', success, fail, data, { dataType: 'text' }); } /** * Удаляет торрент с сервера * @param {string} hash - хэш торрента * @param {function} success - функция при успешном удалении * @param {function} fail - функция при ошибке удаления */ function remove$4(hash, success, fail) { var data = JSON.stringify({ action: 'rem', hash: hash }); clear$d(); network$b.silent(url$5() + '/torrents', success, fail, data, { dataType: 'text' }); } /** * Парсит данные торрента и возвращает объект с информацией о нем * @param {object} data - данные торрента * @returns {object} - объект с информацией о торрента */ function parse$b(data) { var result = EpisodeParser.parse(data); result.hash = Utils$1.hash(result.hash_string); return result; } /** * Очищает имена файлов торрента от лишних символов и возвращает массив с объектами файлов * @param {array} files - массив с объектами файлов торрента * @returns {array} - массив с объектами файлов торрента с очищенными именами */ function clearFileName(files) { var combo = []; files.forEach(function (element) { var spl = element.path.split('/'); var nam = spl[spl.length - 1].split('.'); if (nam.length > 1) nam.pop(); nam = nam.join('.'); element.path_human = Utils$1.pathToNormalTitle(nam, false).trim(); if (spl.length > 1) { spl.pop(); element.folder_name = Utils$1.pathToNormalTitle(spl.pop(), false).trim(); } }); if (files.length > 1) { files.forEach(function (element) { var spl = element.path_human.split(' '); for (var i = spl.length - 1; i >= 0; i--) { var com = spl.join(' '); if (combo.indexOf(com) == -1) combo.push(com); spl.pop(); } }); combo.sort(function (a, b) { return a.length > b.length ? -1 : a.length < b.length ? 1 : 0; }); var _loop = function _loop(i) { var com = combo[i]; var len = files.filter(function (f) { return f.path_human.slice(0, com.length) == com; }).length; if (len < files.length) Arrays.remove(combo, com); }; for (var i = combo.length - 1; i >= 0; i--) { _loop(i); } files.forEach(function (element) { for (var _i = 0; _i < combo.length; _i++) { var com = combo[_i]; var inx = element.path_human.indexOf(com); if (inx >= 0 && com !== element.path_human) { element.path_human = element.path_human.slice(com.length).trim(); break; } } }); } return files; } /** * Очищает очередь запросов к серверу * @returns {void} */ function clear$d() { network$b.clear(); } /** * Показывает модальное окно с информацией об ошибке соединения с сервером * @returns {void} */ function error$3() { var temp = Template.get('torrent_error', { ip: ip() }); var list = temp.find('.torrent-checklist__list > li'); var info = temp.find('.torrent-checklist__info > div'); var next = temp.find('.torrent-checklist__next-step'); var prog = temp.find('.torrent-checklist__progress-bar > div'); var comp = temp.find('.torrent-checklist__progress-steps'); var btn = temp.find('.selector'); var position = -2; function makeStep() { position++; list.slice(0, position + 1).addClass('wait'); var total = list.length; comp.text(Lang.translate('torrent_error_made') + ' ' + Math.max(0, position) + ' ' + Lang.translate('torrent_error_from') + ' ' + total); if (position > list.length) { Modal.close(); Controller.toggle('content'); } else if (position >= 0) { info.addClass('hide'); info.eq(position).removeClass('hide'); var next_step = list.eq(position + 1); prog.css('width', Math.round(position / total * 100) + '%'); list.slice(0, position).addClass('check'); btn.text(position < total ? Lang.translate('torrent_error_next') : Lang.translate('torrent_error_complite')); next.text(next_step.length ? '- ' + next_step.text() : ''); } } makeStep(); btn.on('hover:enter', function () { makeStep(); }); Modal.title(Lang.translate('torrent_error_connect')); Modal.update(temp); Controller.add('modal', { invisible: true, toggle: function toggle() { Controller.collectionSet(temp); Controller.collectionFocus(false, temp); }, back: function back() { Modal.close(); Controller.toggle('content'); } }); Controller.toggle('modal'); } var Torserver = { ip: ip, my: my, add: add$e, url: url$5, hash: hash$1, files: files$1, clear: clear$d, drop: drop, stream: stream, remove: remove$4, connected: connected, parse: parse$b, error: error$3, cache: cache$1, gstWork: gstWork, clearFileName: clearFileName, toPlayUrl: toPlayUrl, viewed: viewed$2, viewedSet: viewedSet }; /** * Добавляет шапку с кнопкой назад * @param {string} title - заголовок шапки * @param {boolean} js - возвращать HTMLElement объект * @returns {HTMLElement|jQuery} - элемент шапки */ function HeadBackward (title, js) { var head = Template[js ? 'js' : 'get']('head_backward', { title: title }); head.find('.head-backward__button').on('click', Controller.back.bind(Controller)); return head; } /** * Статическая машина состояний * @param {Object} object - Объект состояний и переходов * @example let fsm = new StateMachine({ state: 'idle', //начальное состояние transitions: { idle: function() { console.log('idle') this.state = 'loading' this.dispath(this.state) }, loading: function() { console.log('loading') this.state = 'complete' this.dispath(this.state) }, complete: function() { console.log('complete') } } }) fsm.start() // запустить машину */ function StateMachine(object) { this.state = object.state; this.start = function () { this.dispath(this.state); }; this.dispath = function (action_name) { var action = object.transitions[action_name]; if (action) { action.call(this); } else { console.log('invalid action'); } }; } var components$1 = {}; var params$3 = {}; /** * Добавить компонент * @param {{component:string, icon:string, name:string}} data */ function addComponent(data) { components$1[data.component] = data; Template.add('settings_' + data.component, '
    '); } /** * Получить компонент * @param {string} component * @returns {{component:string, icon:string, name:string}} */ function getComponent(component) { return components$1[component]; } /** * Удалить компонент * @param {string} component */ function removeComponent(component) { delete components$1[component]; delete params$3[component]; } /** * Добавить параметр * @param {{component:string, param:{name:string, type:string, values:string|object, default:string|boolean}, field:{name:string, description:string|undefined}, onRender:((item)=>void)|undefined, onChange?:((e)=>void)|undefined}} data */ function addParam(data) { if (!params$3[data.component]) params$3[data.component] = []; params$3[data.component].push(data); if (data.param.type == 'select' || data.param.type == 'input') Params.select(data.param.name, data.param.values, data.param["default"]); if (data.param.type == 'trigger') Params.trigger(data.param.name, data.param["default"]); } /** * Получить параметры * @param {string} component * @returns {[{component:string, param:{name:string, type:string, values:string|object, default:string|boolean}, field:{name:string, description:string|undefined}, onRender:((item)=>void)|undefined, onChange?:((e)=>void)|undefined}]} */ function getParam(component) { return params$3[component]; } /** * Удалить параметры * @param {string} component */ function removeParams(component) { delete params$3[component]; } /** * Получить все компоненты * @returns {{name:{component:string, icon:string, name:string}}} */ function allComponents() { return components$1; } /** * Получить все параметры * @returns {{component:[{component:string, param:{name:string, type:string, values:string|object, default:string|boolean}, field:{name:string, description:string|undefined}, onRender:((item)=>void)|undefined, onChange?:((e)=>void)|undefined}]}} */ function allParams() { return params$3; } var SettingsApi = { allComponents: allComponents, allParams: allParams, addComponent: addComponent, addParam: addParam, getParam: getParam, getComponent: getComponent, removeComponent: removeComponent, removeParams: removeParams }; function Component$2(name) { var component_params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var scrl = new Scroll({ mask: true, over: true, step: 200 }); var comp = Template.get('settings_' + name); var last; scrl.addSwipeDown(function () { Controller.back(); }); /** * Обновить скролл */ function updateScroll() { comp.find('.selector').unbind('hover:focus').on('hover:focus', function (e) { last = e.target; scrl.update($(e.target), true); }).unbind('hover:hover hover:touch').on('hover:hover hover:touch', function (e) { last = e.target; }); } /** * Билдим все события */ function buildEvents() { if (!Platform.is('android')) { comp.find('.is--android').remove(); } if (!Platform.any()) { comp.find('.is--player').remove(); } if (!Platform.is('apple') && !Platform.is('apple_tv') && !Platform.macOS()) { comp.find('.is--infuse').remove(); } if (!Platform.desktop() || Platform.macOS()) { comp.find('.is--nw').remove(); } if (!(Platform.is('android') || Platform.is('browser') || Platform.is('apple_tv') || Platform.desktop())) { comp.find('.is--sound').remove(); } if (!Platform.screen('tv')) { comp.find('.is--tv').remove(); } if (!window.lampa_settings.lang_use) comp.find('[data-name="light_version"]').prev().remove(); scrl.render().find('.scroll__content').addClass('layer--wheight').data('mheight', $('.settings__head')); scrl.render().css('max-height', window.innerWidth <= 480 ? window.innerHeight * 0.6 : 'unset'); Params.bind(comp.find('.selector'), comp); Params.listener.follow('update_scroll', updateScroll); Params.listener.follow('update_scroll_position', function () { if (last) scrl.update(last, true); }); updateScroll(); } /** * Добавляем пользовательские параметры */ function addParams() { var params = SettingsApi.getParam(name); if (params) { params.forEach(function (data) { var item; if (data.param.type == 'select') { item = $("
    \n
    ").concat(data.field.name, "
    \n
    \n
    ")); } if (data.param.type == 'trigger') { item = $("
    \n
    ").concat(data.field.name, "
    \n
    \n
    ")); } if (data.param.type == 'input') { item = $("
    \n
    ").concat(data.field.name, "
    \n
    \n
    ")); } if (data.param.type == 'title') { item = $("
    ".concat(data.field.name, "
    ")); } if (data.param.type == 'static') { item = $("
    \n
    ".concat(data.field.name, "
    \n
    ")); } if (data.param.type == 'button') { item = $("
    \n
    ").concat(data.field.name, "
    \n
    ")); } if (item) { if (data.field.description) item.append("
    ".concat(data.field.description, "
    ")); if (typeof data.onRender == 'function') data.onRender(item); if (typeof data.onChange == 'function') item.data('onChange', data.onChange); comp.append(item); } }); } } /** * Стартуем */ function start() { addParams(); buildEvents(); if (typeof component_params.last_index !== 'undefined' && component_params.last_index > 0) last = comp.find('.selector').eq(component_params.last_index)[0]; Controller.add('settings_component', { toggle: function toggle() { Controller.collectionSet(comp); Controller.collectionFocus(last, comp); }, up: function up() { Navigator.move('up'); }, down: function down() { Navigator.move('down'); }, back: function back() { scrl.destroy(); comp.remove(); Params.listener.remove('update_scroll', updateScroll); if (component_params.onBack) component_params.onBack();else Controller.toggle('settings'); } }); } start(); /** * Уничтожить */ this.destroy = function () { scrl.destroy(); comp.remove(); comp = null; Params.listener.remove('update_scroll', updateScroll); }; /** * Рендер * @returns {object} */ this.render = function () { scrl.append(comp); return scrl.render(); }; } function Main$2() { var _this = this; var comp; var scrl = new Scroll({ mask: true, over: true, step: 200 }); var last; /** * Создать */ this.create = function () { comp = Template.get('settings_main'); if (!window.lampa_settings.account_use) comp.find('[data-component="account"]').remove(); if (!window.lampa_settings.plugins_use) comp.find('[data-component="plugins"]').remove(); if (!Lampa.Platform.is('apple_tv')) comp.find('[data-component="appletv"]').remove(); if (!window.lampa_settings.torrents_use) { comp.find('[data-component="parser"]').remove(); comp.find('[data-component="server"]').remove(); } scrl.render().css('max-height', window.innerWidth <= 480 ? window.innerHeight * 0.6 : 'unset'); scrl.append(comp); scrl.addSwipeDown(_this.swipeAction); _this.update(); }; /** * Обновить события */ this.update = function () { var components = SettingsApi.allComponents(); for (var name in components) { var aded = components[name]; if (!comp.find('[data-component="' + name + '"]').length) { var item = $("
    \n
    \n ").concat(aded.icon, "\n
    \n
    ").concat(aded.name, "
    \n
    ")); if (aded.before) comp.find('[data-component="' + aded.before + '"]').before(item);else if (aded.after) comp.find('[data-component="' + aded.after + '"]').after(item);else comp.append(item); } } comp.find('.selector').unbind('hover:focus').on('hover:focus', function (event) { last = event.target; scrl.update($(event.target), true); }).unbind('hover:hover hover:touch').on('hover:hover hover:touch', function (event) { last = event.target; Navigator.focused(event.target); }).not('[data-static]').unbind('hover:enter').on('hover:enter', function (event) { _this.render().detach(); _this.onCreate($(event.target).data('component')); }); }; /** * Сделать активным */ this.active = function () { Controller.collectionSet(comp); Controller.collectionFocus(last, comp); scrl.height($('.settings__head')); }; /** * Рендер * @returns {object} */ this.render = function () { return scrl.render(); }; } var permit = {}; // Получить аккаунт Object.defineProperty(permit, 'account', { get: function get() { return Storage.get('account', '{}'); } }); // Получить профиль аккаунта Object.defineProperty(permit, 'profile', { get: function get() { return permit.account.profile || {}; } }); // Получить данные пользователя Object.defineProperty(permit, 'user', { get: function get() { return Storage.get('account_user', '{}'); } }); // Получить токен аккаунта Object.defineProperty(permit, 'token', { get: function get() { return permit.account.token; } }); // Пользователь залогинен и разрешено использование аккаунта Object.defineProperty(permit, 'access', { get: function get() { return permit.token && window.lampa_settings.account_use; } }); // Пользователь включил синхронизацию Object.defineProperty(permit, 'use', { get: function get() { return permit.access && Storage.field('account_use'); } }); // Синхронизация включена и разрешено использование синхронизации Object.defineProperty(permit, 'sync', { get: function get() { return permit.use && window.lampa_settings.account_sync; } }); // Детский профиль Object.defineProperty(permit, 'child', { get: function get() { return permit.access && permit.profile.child; } }); // Детский профиль - маленький ребенок (до 14 лет) Object.defineProperty(permit, 'child_small', { get: function get() { return permit.child ? permit.profile.age <= 14 : false; } }); var data$3 = {}; data$3.type = { title: '#{title_type}', items: [{ title: '#{menu_movies}', selected: true, cat: 'movie' }, { title: '#{menu_multmovie}', cat: 'multmovie' }, { title: '#{menu_tv}', cat: 'tv' }, { title: '#{menu_multtv}', cat: 'multtv' }, { title: '#{menu_anime}', cat: 'anime' }] }; data$3.rating = { title: '#{title_rating}', items: [{ title: '#{filter_any}' }, { title: '#{filter_rating_from} 8', start: 8 }, { title: '#{filter_rating_from} 6', start: 6 }, { title: '#{filter_rating_from} 4', start: 4 }, { title: '#{filter_rating_from} 2', start: 2 }, { title: '#{filter_rating_from} 1 #{filter_rating_to} 3', voite: '1-3' }, { title: '#{filter_rating_from} 3 #{filter_rating_to} 6', voite: '3-6' }, { title: '#{filter_rating_from} 6 #{filter_rating_to} 8', voite: '6-8' }, { title: '#{filter_rating_from} 8 #{filter_rating_to} 9', voite: '8-9' }] }; data$3.pgrating = { title: '#{title_pgrating}', items: [{ title: '#{filter_any}' }] }; data$3.language = { title: '#{title_language}', items: [{ title: '#{filter_lang_ru}', code: 'ru' }, { title: '#{filter_lang_uk}', code: 'uk' }, { title: '#{filter_lang_en}', code: 'en' }, { title: '#{filter_lang_be}', code: 'be' }, { title: '#{filter_lang_zh}', code: 'zh|cn' }, { title: '#{filter_lang_ja}', code: 'ja' }, { title: '#{filter_lang_ko}', code: 'ko' }, { title: '#{filter_lang_af}', code: 'af' }, { title: '#{filter_lang_sq}', code: 'sq' }, { title: '#{filter_lang_ar}', code: 'ar' }, { title: '#{filter_lang_az}', code: 'az' }, { title: '#{filter_lang_hy}', code: 'hy' }, { title: '#{filter_lang_ba}', code: 'ba' }, { title: '#{filter_lang_bg}', code: 'bg' }, { title: '#{filter_lang_bn}', code: 'bn' }, { title: '#{filter_lang_bs}', code: 'bs' }, { title: '#{filter_lang_ca}', code: 'ca' }, { title: '#{filter_lang_ce}', code: 'ce' }, { title: '#{filter_lang_cs}', code: 'cs' }, { title: '#{filter_lang_da}', code: 'da' }, { title: '#{filter_lang_ka}', code: 'ka' }, { title: '#{filter_lang_de}', code: 'de' }, { title: '#{filter_lang_el}', code: 'el' }, { title: '#{filter_lang_es}', code: 'es' }, { title: '#{filter_lang_et}', code: 'et' }, { title: '#{filter_lang_fa}', code: 'fa' }, { title: '#{filter_lang_fi}', code: 'fi' }, { title: '#{filter_lang_fr}', code: 'fr' }, { title: '#{filter_lang_ga}', code: 'ga' }, { title: '#{filter_lang_gl}', code: 'gl' }, { title: '#{filter_lang_gn}', code: 'gn' }, { title: '#{filter_lang_he}', code: 'he' }, { title: '#{filter_lang_hi}', code: 'hi' }, { title: '#{filter_lang_hr}', code: 'hr' }, { title: '#{filter_lang_hu}', code: 'hu' }, { title: '#{filter_lang_id}', code: 'id' }, { title: '#{filter_lang_is}', code: 'is' }, { title: '#{filter_lang_it}', code: 'it' }, { title: '#{filter_lang_kk}', code: 'kk' }, { title: '#{filter_lang_ks}', code: 'ks' }, { title: '#{filter_lang_ku}', code: 'ku' }, { title: '#{filter_lang_ky}', code: 'ky' }, { title: '#{filter_lang_lt}', code: 'lt' }, { title: '#{filter_lang_lv}', code: 'lv' }, { title: '#{filter_lang_mi}', code: 'mi' }, { title: '#{filter_lang_mk}', code: 'mk' }, { title: '#{filter_lang_mn}', code: 'mn' }, { title: '#{filter_lang_mt}', code: 'mt' }, { title: '#{filter_lang_no}', code: 'no|nb|nn' }, { title: '#{filter_lang_ne}', code: 'ne' }, { title: '#{filter_lang_nl}', code: 'nl' }, { title: '#{filter_lang_pa}', code: 'pa' }, { title: '#{filter_lang_pl}', code: 'pl' }, { title: '#{filter_lang_ps}', code: 'ps' }, { title: '#{filter_lang_pt}', code: 'pt' }, { title: '#{filter_lang_ro}', code: 'ro' }, { title: '#{filter_lang_si}', code: 'si' }, { title: '#{filter_lang_sk}', code: 'sk' }, { title: '#{filter_lang_sl}', code: 'sl' }, { title: '#{filter_lang_sm}', code: 'sm' }, { title: '#{filter_lang_so}', code: 'so' }, { title: '#{filter_lang_sr}', code: 'sr' }, { title: '#{filter_lang_sv}', code: 'sv' }, { title: '#{filter_lang_sw}', code: 'sw' }, { title: '#{filter_lang_ta}', code: 'ta' }, { title: '#{filter_lang_tg}', code: 'tg' }, { title: '#{filter_lang_th}', code: 'th' }, { title: '#{filter_lang_tk}', code: 'tk' }, { title: '#{filter_lang_tr}', code: 'tr' }, { title: '#{filter_lang_tt}', code: 'tt' }, { title: '#{filter_lang_ur}', code: 'ur' }, { title: '#{filter_lang_uz}', code: 'uz' }, { title: '#{filter_lang_vi}', code: 'vi' }, { title: '#{filter_lang_yi}', code: 'yi' }] }; data$3.genres_movie = { title: '#{title_genre}', items: [{ "id": 28, "title": "#{filter_genre_ac}", checkbox: true }, { "id": 12, "title": "#{filter_genre_ad}", checkbox: true }, { "id": 16, "title": "#{filter_genre_mv}", checkbox: true }, { "id": 35, "title": "#{filter_genre_cm}", checkbox: true }, { "id": 80, "title": "#{filter_genre_cr}", checkbox: true }, { "id": 99, "title": "#{filter_genre_dc}", checkbox: true }, { "id": 18, "title": "#{filter_genre_dr}", checkbox: true }, { "id": 10751, "title": "#{filter_genre_fm}", checkbox: true }, { "id": 14, "title": "#{filter_genre_fe}", checkbox: true }, { "id": 36, "title": "#{filter_genre_hi}", checkbox: true }, { "id": 27, "title": "#{filter_genre_ho}", checkbox: true }, { "id": 10402, "title": "#{filter_genre_mu}", checkbox: true }, { "id": 9648, "title": "#{filter_genre_de}", checkbox: true }, { "id": 10749, "title": "#{filter_genre_md}", checkbox: true }, { "id": 878, "title": "#{filter_genre_fa}", checkbox: true }, { "id": 10770, "title": "#{filter_genre_tv}", checkbox: true }, { "id": 53, "title": "#{filter_genre_tr}", checkbox: true }, { "id": 10752, "title": "#{filter_genre_mi}", checkbox: true }, { "id": 37, "title": "#{filter_genre_ve}", checkbox: true }] }; data$3.genres_tv = { title: '#{title_genre}', items: [{ "id": 10759, "title": "#{filter_genre_aa}", checkbox: true }, { "id": 16, "title": "#{filter_genre_mv}", checkbox: true }, { "id": 35, "title": "#{filter_genre_cm}", checkbox: true }, { "id": 80, "title": "#{filter_genre_cr}", checkbox: true }, { "id": 99, "title": "#{filter_genre_dc}", checkbox: true }, { "id": 18, "title": "#{filter_genre_dr}", checkbox: true }, { "id": 10751, "title": "#{filter_genre_fm}", checkbox: true }, { "id": 10762, "title": "#{filter_genre_ch}", checkbox: true }, { "id": 9648, "title": "#{filter_genre_de}", checkbox: true }, { "id": 10763, "title": "#{filter_genre_nw}", checkbox: true }, { "id": 10764, "title": "#{filter_genre_rs}", checkbox: true }, { "id": 10765, "title": "#{filter_genre_hf}", checkbox: true }, { "id": 10766, "title": "#{filter_genre_op}", checkbox: true }, { "id": 10767, "title": "#{filter_genre_tc}", checkbox: true }, { "id": 10768, "title": "#{filter_genre_mp}", checkbox: true }, { "id": 37, "title": "#{filter_genre_ve}", checkbox: true }] }; data$3.sort = { title: '#{filter_sorted}', items: [{ title: '#{filter_any}' }, { title: '#{title_new}', sort: 'now' }, { title: '#{title_now_watch}', sort: 'now_playing' }, { title: '#{title_in_top}', sort: 'top' }, { title: '#{title_ongoing}', sort: 'airing' }] }; data$3.quality = { title: '#{player_quality}', items: [{ title: '#{filter_any}' }, { title: '#{title_in_high_quality}', uhd: true }] }; data$3.year = { title: '#{title_year}', items: [{ title: '#{filter_any}', any: true }] }; var i = 100, y = new Date().getFullYear(); for (var a = 0; a < 5; a++) { data$3.year.items.push({ title: y - a }); } while (i -= 5) { var end = y - (99 - i); data$3.year.items.push({ title: end + 5 + '-' + end }); } for (var _a = 18; _a >= 0; _a -= 3) { data$3.pgrating.items.push({ title: _a + '+', pg: _a }); } for (var _a2 = 15; _a2 >= 0; _a2 -= 3) { data$3.pgrating.items.push({ title: '#{filter_rating_from} ' + _a2 + ' #{filter_rating_to} ' + (_a2 + 3), pg: _a2 + '-' + (_a2 + 3) }); } data$3.language.items.forEach(function (i) { return i.checkbox = true; }); /** * Главное меню фильтрации */ function main$3() { for (var i in data$3) { selected$1(data$3[i]); } var cat = data$3.type.items.find(function (s) { return s.selected; }).cat; var type = cat.indexOf('movie') >= 0 ? 'movie' : 'tv'; var items = [{ title: Lang.translate('search_start'), search: true }, data$3.type, data$3.rating, data$3['genres_' + type], data$3.language, data$3.year]; if (Storage.field('source') == 'cub') items.push(data$3.pgrating, data$3.sort, data$3.quality); items.forEach(function (itm) { itm.title = Lang.translate(itm.title); if (itm.subtitle) itm.subtitle = Lang.translate(itm.subtitle); if (itm.items) { itm.items.forEach(function (inr) { inr.title = Lang.translate(inr.title); }); } }); Select.show({ title: Lang.translate('title_filter'), items: items, onBack: function onBack() { Controller.toggle('content'); }, onSelect: function onSelect(a) { if (a.search) search$6();else submenu$1(a); } }); } /** * Запрос для TMDB * @returns {string} - строка запроса */ function queryForTMDB() { var query = []; var cat = data$3.type.items.find(function (s) { return s.selected; }).cat; var type = cat.indexOf('movie') >= 0 ? 'movie' : 'tv'; var genres = []; var languages = []; data$3.rating.items.forEach(function (a) { if (a.selected && (a.voite || a.start)) { if (a.start) { query.push('vote_average.gte=' + a.start); } else { query.push('vote_average.gte=' + a.voite.split('-')[0]); query.push('vote_average.lte=' + a.voite.split('-')[1]); } } }); data$3.language.items.forEach(function (a) { if (a.checked) languages.push(a.code); }); data$3.year.items.forEach(function (a) { if (a.selected && !a.any) { var need = type == 'movie' ? 'primary_release_date' : 'first_air_date'; if (a.title.indexOf('-') >= 0) { query.push(need + '.lte=' + a.title.split('-')[0] + '-12-31'); query.push(need + '.gte=' + a.title.split('-')[1] + '-01-01'); } else { query.push((type == 'movie' ? 'primary_release_year' : 'first_air_date_year') + '=' + a.title); } } }); data$3['genres_' + type].items.forEach(function (a) { if (a.checked) genres.push(a.id); }); if (cat == 'multmovie' || cat == 'multtv' && genres.indexOf(16) == -1) genres.push(16); if (cat == 'movie' || cat == 'tv') query.push('without_genres=16'); if (genres.length) { query.push('with_genres=' + genres.join(',')); } if (cat == 'anime' && languages.indexOf('ja') == -1) languages.push('ja'); if (languages.length) { query.push('with_original_language=' + languages.join('|')); } return 'discover/' + type + '?' + query.join('&'); } /** * Запрос для CUB * @returns {string} - строка запроса */ function queryForCUB() { var query = []; var cat = data$3.type.items.find(function (s) { return s.selected; }).cat; var type = cat.indexOf('movie') >= 0 ? 'movie' : 'tv'; var genres = []; var sort = data$3.sort.items.find(function (s) { return s.selected && s.sort; }); var quality = data$3.quality.items.find(function (s) { return s.selected && s.uhd; }); var languages = []; data$3.rating.items.forEach(function (a) { if (a.selected && (a.voite || a.start)) { if (a.start) { query.push('vote=' + a.start); } else { query.push('vote=' + a.voite.split('-')[0] + '-' + a.voite.split('-')[1]); } } }); data$3.language.items.forEach(function (a) { if (a.checked) languages.push(a.code.split('|')[0]); }); data$3.year.items.forEach(function (a) { if (a.selected && !a.any) { if (a.title.indexOf('-') >= 0) { query.push('airdate=' + a.title.split('-')[1] + '-' + a.title.split('-')[0]); } else { query.push('airdate=' + a.title); } } }); data$3.pgrating.items.forEach(function (a) { if (a.selected) { if (a.title.indexOf('-') >= 0) { query.push('pgrating=' + a.pg.split('-')[0] + '-' + a.pg.split('-')[1]); } else { query.push('pgrating=' + a.pg); } } }); data$3['genres_' + type].items.forEach(function (a) { if (a.checked) genres.push(a.id); }); if ((cat == 'multmovie' || cat == 'multtv' || permit.child_small) && genres.indexOf(16) == -1) genres.push(16); if ((cat == 'movie' || cat == 'tv') && !permit.child_small) query.push('without_genres=16'); if (genres.length) { query.push('genre=' + genres.join(',')); } if (cat == 'anime') type = 'anime'; if (languages.length) { query.push('language=' + languages.join(',')); } if (sort) query.push('sort=' + sort.sort); if (quality) query.push('uhd=true'); return '?cat=' + type + '&' + query.join('&'); } /** * Запуск поиска */ function search$6() { Controller.toggle('content'); var source = Storage.field('source'); var query = source == 'cub' ? queryForCUB() : queryForTMDB(); var activity = { url: query, title: Lang.translate('title_filter'), component: 'category_full', source: source == 'cub' ? 'cub' : 'tmdb', card_type: true, page: 1 }; var object = Activity.active(); if (object.component == 'category_full' && (object.url.indexOf('discover') == 0 || object.url.indexOf('?cat=') == 0)) Activity.replace(activity, true);else Activity.push(activity); } /** * Выбор элемента * @param {Array} where - массив элементов * @param {Object} a - выбранный элемент */ function select$4(where, a) { where.forEach(function (element) { element.selected = false; }); a.selected = true; } /** * Обновление подзаголовка * @param {Object} where - объект с массивом items */ function selected$1(where) { var title = []; where.items.forEach(function (a) { if (a.selected || a.checked) title.push(a.title); }); where.subtitle = title.length ? title.join(', ') : Lang.translate('nochoice'); } /** * Подменю * @param {Object} item - объект элемента */ function submenu$1(item) { Select.show({ title: item.title, items: item.items, onBack: main$3, onSelect: function onSelect(a) { select$4(item.items, a); main$3(); } }); } /** * Запуск фильтра */ function show$d() { main$3(); } var Filter$1 = { show: show$d }; var menu$4; var timer$8; function init$1i(list) { menu$4 = list; observe$3(); } function start$b() { var list = $(''); menu$4.find('.menu__item').each(function () { var item_orig = $(this); var item_clone = $(this).clone(); var item_sort = $("
    \n
    \n
    ".concat(item_clone.find('.menu__text').text(), "
    \n
    \n \n \n \n
    \n
    \n \n \n \n
    \n
    \n \n \n \n \n
    \n
    ")); item_sort.find('.menu-edit-list__icon').append(item_clone.find('.menu__ico').html()); item_sort.find('.move-up').on('hover:enter', function () { var prev = item_sort.prev(); if (prev.length) { item_sort.insertBefore(prev); item_orig.insertBefore(item_orig.prev()); } }); item_sort.find('.move-down').on('hover:enter', function () { var next = item_sort.next(); if (next.length) { item_sort.insertAfter(next); item_orig.insertAfter(item_orig.next()); } }); item_sort.find('.toggle').on('hover:enter', function () { item_orig.toggleClass('hidden'); item_sort.find('.dot').attr('opacity', item_orig.hasClass('hidden') ? 0 : 1); }).find('.dot').attr('opacity', item_orig.hasClass('hidden') ? 0 : 1); list.append(item_sort); }); Modal.open({ title: Lang.translate('extensions_edit'), html: list, size: 'small', scroll_to_center: true, onBack: function onBack() { Modal.close(); save$4(); Controller.toggle('menu'); } }); } function order() { var items = Storage.get('menu_sort', '[]'); if (items.length) { items.forEach(function (item) { var el = $('.menu__item:contains("' + item + '")', menu$4); if (el.length) el.appendTo(menu$4); }); } } function hide$3() { var items = Storage.get('menu_hide', '[]'); $('.menu__item', menu$4).removeClass('hidden'); if (items.length) { items.forEach(function (item) { var el = $('.menu__item:contains("' + item + '")', menu$4); if (el.length) el.addClass('hidden'); }); } } function save$4() { var sort = []; var hide = []; $('.menu__item', menu$4).each(function () { var name = $(this).find('.menu__text').text().trim(); sort.push(name); if ($(this).hasClass('hidden')) { hide.push(name); } }); Storage.set('menu_sort', sort); Storage.set('menu_hide', hide); } function update$g() { order(); hide$3(); } function observe$3() { clearTimeout(timer$8); timer$8 = setTimeout(function () { var memory = Storage.get('menu_sort', '[]'); var anon = []; $('.menu__item', menu$4).each(function () { anon.push($(this).find('.menu__text').text().trim()); }); anon.forEach(function (item) { if (memory.indexOf(item) == -1) memory.push(item); }); Storage.set('menu_sort', memory); update$g(); }, 500); } var Editor = { init: init$1i, start: start$b, update: update$g, observe: observe$3 }; var Router$1 = /*#__PURE__*/function () { function Router() { _classCallCheck(this, Router); this.routes = []; } _createClass(Router, [{ key: "add", value: function add(name, callback) { if (typeof name === 'string' && typeof callback === 'function') { this.routes.push({ name: name, callback: callback }); } else { console.error('Invalid route definition. Expected a string and a function.'); } } }, { key: "get", value: function get(name) { return this.routes.find(function (route) { return route.name === name; }); } }, { key: "call", value: function call(name, data) { var route = this.get(name); if (route) { var push = route.callback(data); Arrays.extend(push, { url: data.url || '', component: route.name, source: data.source || Storage.field('source'), page: data.page || 1 }); Activity.push(push); } else { Arrays.extend(data, { url: data.url || '', component: name, source: data.source || Storage.field('source'), page: data.page || 1 }); Activity.push(data); } } }]); return Router; }(); var router = new Router$1(); router.add('full', function (data) { return { id: data.id, method: data.original_name ? 'tv' : 'movie', card: data }; }); router.add('category_full', function (data) { return { genres: data.genres || '', keywords: data.keywords || '', companies: data.companies || '', query: data.query || '', filter: data.filter || '', title: data.title || Lang.translate('title_category') }; }); router.add('category', function (data) { return _objectSpread2(_objectSpread2({}, data), {}, { title: data.title || Lang.translate('title_category') }); }); router.add('favorite', function (data) { return { title: data.title || Lang.translate('title_' + data.type), type: data.type, filter: data.filter || '' }; }); router.add('episodes', function (data) { return { title: Lang.translate('title_episodes'), card: data }; }); router.add('discuss', function (data) { return { card: data, id: data.id, method: data.name ? 'tv' : 'movie', page: data.page || 2 }; }); router.add('company', function (data) { return { url: data.url || (data.card.name ? 'tv' : 'movie'), id: data.id, source: data.card.source, title: Lang.translate('title_company') }; }); router.add('recomend', function (data) { return { title: Lang.translate('title_recomend_watch'), page: data.page || 1, media: data.media || 'movie' }; }); router.add('actor', function (data) { return { id: data.id, job: (data.known_for_department || 'actor').toLowerCase() }; }); var html$m; var last$4; var scroll$3; var visible_timer; function menuAlwaysVisible() { return Platform.screen('tv') && Storage.field('menu_always'); } var menu_items = [{ action: 'main', title: 'menu_main', sprite: 'home' }, { action: 'feed', title: 'menu_feed', sprite: 'feed' }, { action: 'movie', title: 'menu_movies', sprite: 'movie' }, { action: 'cartoon', title: 'menu_multmovie', sprite: 'cartoon' }, { action: 'tv', title: 'menu_tv', sprite: 'tv' }, { action: 'myperson', title: 'title_persons', sprite: 'person' }, { action: 'catalog', title: 'menu_catalog', sprite: 'catalog' }, { action: 'filter', title: 'menu_filter', sprite: 'filter' }, { action: 'relise', title: 'menu_relises', sprite: 'hd' }, { action: 'anime', title: 'menu_anime', sprite: 'anime' }, { action: 'favorite', title: 'settings_input_links', sprite: 'favorite' }, { action: 'history', title: 'menu_history', sprite: 'history' }, { action: 'subscribes', title: 'title_subscribes', sprite: 'subscribes' }, { action: 'timetable', title: 'menu_timeline', sprite: 'calendar' }, { action: 'mytorrents', title: 'menu_torrents', sprite: 'torrent' }]; /** * Инициализация меню * @returns {void} */ function init$1h() { html$m = Template.get('menu'); scroll$3 = new Scroll({ mask: true, over: true }); // Фильтрация пунктов меню в зависимости от настроек menu_items = menu_items.filter(function (item) { if (!window.lampa_settings.torrents_use && item.action == 'mytorrents') return false; if (window.lampa_settings.disable_features.persons && item.action == 'myperson') return false; if (window.lampa_settings.disable_features.subscribe && item.action == 'subscribes') return false; if (!window.lampa_settings.feed && item.action == 'feed') return false; if (!Lang.selected(['ru', 'uk', 'be']) && (item.action == 'relise' || item.action == 'anime' || item.action == 'feed')) return false; return true; }); // Добавление кнопок меню menu_items.forEach(function (item) { addButton(""), Lang.translate(item.title)).attr('data-action', item.action); }); // Отправка события для плагинов Lampa.Listener.send('menu', { type: 'start', body: html$m }); // Инициализация редактора меню Editor.init($('.menu__list:eq(0)', html$m)); // Наблюдатель за добавлением новых селекторов в меню observe$2(); // Инициализация контроллера меню Controller.add('menu', { toggle: function toggle() { Controller.collectionSet(html$m); Controller.collectionFocus(last$4, html$m, true); clearTimeout(visible_timer); $('.wrap__left').removeClass('wrap__left--hidden'); $('body').toggleClass('menu--open', true); }, right: function right() { Controller.toggle('content'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('head'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down'); }, gone: function gone() { $('body').toggleClass('menu--open', false); if (!menuAlwaysVisible()) { visible_timer = setTimeout(function () { $('.wrap__left').addClass('wrap__left--hidden'); }, 300); } }, back: function back() { Activity.backward(); } }); // Закрытие меню по клику вне его области $('body').on('mousedown', function (e) { if (DeviceInput.canClick(e.originalEvent) && opened$2()) { if (e.originalEvent.clientX > html$m.outerWidth()) close$9(); } }); scroll$3.minus(); scroll$3.append(html$m); if (menuAlwaysVisible()) { $('.wrap__left').removeClass('wrap__left--hidden'); } // Отправка события для плагинов Lampa.Listener.send('menu', { type: 'end' }); Lampa.Listener.follow('app', function (e) { if (e.type == 'ready') ready(); }); } /** * Следит за добавлением новых селекторов в меню * @returns {void} */ function observe$2() { if (typeof MutationObserver == 'undefined') return; var observer = new MutationObserver(function (mutations) { for (var i = 0; i < mutations.length; i++) { var mutation = mutations[i]; if (mutation.type == 'childList' && !mutation.removedNodes.length) { var selectors = Array.from(mutation.target.querySelectorAll('.selector')).filter(function (s) { return !s.checked; }); if (selectors.length) Editor.observe(); selectors.forEach(function (s) { s.checked = true; if (!$(s).data('binded_events')) { $(s).on('hover:focus', function (e) { last$4 = e.target; scroll$3.update($(e.target), true); }).on('hover:hover hover:touch hover:enter', function (e) { last$4 = e.target; }); } }); } } }); observer.observe(html$m[0], { childList: true, subtree: true }); } /** * Проверяет, нужно ли открывать новый компонент или обновлять текущий * @param {string} action Действие, которое нужно выполнить * @param {Array} name Название компонента(ов), которые нужно проверить * @returns {boolean|void} Нужно ли открывать новый компонент или обновлять текущий */ function prepared(action, name) { if (name.indexOf(action) >= 0) { var comp = Lampa.Activity.active().component; if (name.indexOf(comp) >= 0) Activity.replace();else return true; } } /** * Готово к работе * @returns {void} */ function ready() { html$m.find('.selector').data('binded_events', true).on('hover:enter', function (e) { var action = $(e.target).data('action'); Lampa.Listener.send('menu', { type: 'action', action: action, target: e.target, abort: function abort() { action = null; } }); if (action == 'catalog') catalog(); if (action == 'movie' || action == 'tv' || action == 'anime') { router.call('category', { url: action, title: (action == 'movie' ? Lang.translate('menu_movies') : action == 'anime' ? Lang.translate('menu_anime') : Lang.translate('menu_tv')) + ' - ' + Storage.field('source').toUpperCase(), source: action == 'anime' ? 'cub' : Storage.field('source') }); } if (action == 'cartoon') { router.call('category', { url: 'movie', title: Lang.translate('menu_multmovie') + ' - ' + Storage.field('source').toUpperCase(), genres: 16 }); } if (prepared(action, ['main'])) { router.call('main', { title: Lang.translate('title_main') + ' - ' + Storage.field('source').toUpperCase() }); } if (prepared(action, ['myperson'])) { router.call('myperson', { title: Lang.translate('title_persons') }); } if (action == 'search') Controller.toggle('search'); if (action == 'settings') { ParentalControl.personal('settings', function () { Controller.toggle('settings'); }, false, true); } if (action == 'about') { var about = Template.get('about'); if (window.lampa_settings.white_use) { about.find('.about__contacts > div:eq(1)').remove(); } if (Platform.is('android')) { about.find('.platform_android').removeClass('hide'); about.find('.version_android').text(Platform.version('android')); } about.find('.version_app').text(Platform.version('app')); Modal.open({ about: true, title: Lang.translate('title_about'), html: about, size: 'medium', onBack: function onBack() { Modal.close(); Controller.toggle('content'); } }); } if (action == 'favorite') { ParentalControl.personal('bookmarks', function () { if (prepared('bookmarks', ['bookmarks'])) { router.call('bookmarks', { title: Lang.translate('settings_input_links') }); } }, false, true); } if (action == 'history') { ParentalControl.personal('bookmarks', function () { if (prepared('favorite', ['favorite']) || prepared('bookmarks', ['bookmarks'])) { router.call('favorite', { title: Lang.translate('title_history'), type: 'history' }); } }, false, true); } if (action == 'subscribes') { router.call('subscribes', { title: Lang.translate('title_subscribes') }); } if (prepared(action, ['timetable'])) { router.call('timetable', { title: Lang.translate('title_timetable') }); } if (prepared(action, ['feed'])) { router.call('feed', { title: Lang.translate('menu_feed') }); } if (prepared(action, ['mytorrents'])) { router.call('mytorrents', { title: Lang.translate('title_mytorrents') }); } if (prepared(action, ['relise'])) { router.call('relise', { title: Lang.translate('title_relises') }); } if (action == 'console') { Controller.toggle('console'); } if (action == 'filter') Filter$1.show(); if (action == 'edit') Editor.start(); }).on('hover:focus', function (e) { last$4 = e.target; scroll$3.update($(e.target), true); }).on('hover:hover hover:touch hover:enter', function (e) { last$4 = e.target; }); } /** * Открывает каталог * @returns {void} */ function catalog() { Api.menu({ source: Storage.field('source') }, function (menu) { Select.show({ title: Lang.translate('title_catalog'), items: menu, onSelect: function onSelect(a) { var tmdb = Storage.field('source') == 'tmdb' || Storage.field('source') == 'cub'; router.call(tmdb ? 'category' : 'category_full', { url: 'movie', title: (a.title || Lang.translate('title_catalog')) + ' - ' + Storage.field('source').toUpperCase(), genres: a.id, id: a.id }); }, onBack: open$8 }); }); } /** * Добавляет элемент в меню * @param {JQuery} element Элемент меню * @param {Function} action Действие при нажатии на элемент * @returns {JQuery} Добавленный элемент меню */ function addElement$1(element, action) { html$m.find('.menu__list:eq(0)').append(element); if (action && typeof action == 'function') element.on('hover:enter', action); return element; } /** * Добавляет кнопку в меню * @param {string} svg_icon SVG иконка кнопки * @param {string} title Название кнопки * @param {Function} action Действие при нажатии на кнопку * @returns {JQuery} Добавленная кнопка меню */ function addButton(svg_icon, title, action) { return addElement$1($("
  • ".concat(svg_icon, "
    ").concat(title, "
  • ")), action); } /** * Переключает меню * @returns {void} */ function toggle$b() { if ($('body').hasClass('menu--open')) Controller.toggle('content');else Controller.toggle('menu'); Lampa.Listener.send('menu', { type: 'toggle' }); } /** * Проверяет, открыто ли меню * @returns {boolean} Открыто ли меню */ function opened$2() { return $('body').hasClass('menu--open'); } /** * Открывает меню * @returns {void} */ function open$8() { if (!opened$2()) toggle$b(); } /** * Закрывает меню * @returns {void} */ function close$9() { if (opened$2()) toggle$b(); } /** * Рендерит меню * @returns {JQuery} Меню */ function render$k() { return scroll$3.render(); } var Menu$1 = { init: Utils$1.onceInit(init$1h), render: render$k, ready: ready, toggle: toggle$b, opened: opened$2, addElement: addElement$1, addButton: addButton, open: open$8, close: close$9 }; var html$l; var last$3; /** * Наблюдение за изменениями в DOM * @returns {void} */ function observe$1() { if (typeof MutationObserver == 'undefined') return; var observer = new MutationObserver(function (mutations) { for (var i = 0; i < mutations.length; i++) { var mutation = mutations[i]; if (mutation.type == 'childList' && !mutation.removedNodes.length) { var selectors = Array.from(mutation.target.querySelectorAll('.selector')); selectors.forEach(function (s) { $(s).unbind('hover:focus hover:hover hover:touch').on('hover:focus hover:hover hover:touch', function (e) { last$3 = e.target; }); }); } } }); observer.observe(html$l[0], { childList: true, subtree: true }); } /** * Инициализация шапки * @returns {void} */ function init$1g() { html$l = Template.get('head'); if (window.local_lampa) html$l.find('.head__logo-icon').append('local'); if (Platform.mouse()) { var back = Template.elem('div', { "class": 'head__backward', children: [Template.js('icon_back')] }); back.on('click', Controller.back.bind(Controller)); html$l.find('.head__body').prepend(back); } Utils$1.time(html$l); html$l.find('.head__logo-icon, .head__menu-icon').on('mousedown', function (e) { if (DeviceInput.canClick(e.originalEvent)) Menu$1.toggle(); }); html$l.find('.full--screen').on('hover:enter', function () { Utils$1.toggleFullscreen(); }).toggleClass('hide', Platform.tv() || Platform.is('android') || !Utils$1.canFullScreen()); observe$1(); Controller.add('head', { toggle: function toggle() { Controller.collectionSet(html$l, false, true); Controller.collectionFocus(last$3, html$l, true); }, right: function right() { Navigator.move('right'); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else Controller.toggle('menu'); }, down: function down() { Controller.toggle('content'); }, back: function back() { Activity.backward(); } }); } /** * Установка заголовка * @param {string} title Заголовок * @returns {void} */ function title$1(title) { html$l.find('.head__title').text(title || ''); } /** * Добавление элемента в шапку * @param {JQuery|HTMLElement|string} element - Элемент * @param {function} action - Действие при нажатии * @returns {JQuery|HTMLElement} - Добавленный элемент */ function addElement(element, action) { html$l.find('.head__actions').prepend(element); if (action && typeof action == 'function') element.on('hover:enter', action); return element; } /** * Добавление иконки в шапку * @param {string} svg_icon - SVG иконка * @param {function} action - Действие при нажатии * @returns {JQuery|HTMLElement} - Добавленная иконка */ function addIcon(svg_icon, action) { return addElement($("
    ".concat(svg_icon, "
    ")), action); } /** * Получение HTML шапки * @param {boolean} js - Вернуть DOM элемент вместо JQuery * @returns {JQuery|HTMLElement} */ function render$j(js) { return js ? html$l[0] : html$l; } var Head = { init: init$1g, render: render$j, title: title$1, addElement: addElement, addIcon: addIcon }; var html$k; var body$1; var listener$i = Subscribe$2(); var last$2 = ''; var _main; /** * Инициализация настроек * @returns {void} */ function init$1f() { html$k = Template.get('settings'); body$1 = html$k.find('.settings__body'); html$k.find('.settings__layer').on('click', function (e) { if (DeviceInput.canClick(e.originalEvent)) Controller.back(); }); _main = new Main$2(); _main.onCreate = create$b; _main.swipeAction = swipeAction; _main.create(); Controller.add('settings', { toggle: function toggle() { _main.render().detach(); _main.update(); listener$i.send('open', { name: 'main', body: _main.render() }); body$1.empty().append(_main.render()); _main.active(); $('body').toggleClass('settings--open', true); html$k.addClass('animate'); Activity.mixState('settings=main'); }, up: function up() { Navigator.move('up'); }, down: function down() { Navigator.move('down'); }, left: function left() { _main.render().detach(); Controller.toggle('content'); }, gone: function gone(to) { if (to !== 'settings_component') { $('body').toggleClass('settings--open', false); html$k.removeClass('animate').removeClass('animate-down'); } }, back: function back() { _main.render().detach(); Controller.toggle('head'); Activity.mixState(); listener$i.send('close'); } }); ParentalControl.add('settings', { title: 'title_settings' }); Head.addIcon(Template.string('icon_settings'), function () { ParentalControl.personal('settings', function () { Controller.toggle('settings'); }, false, true); }).addClass('open--settings'); } function swipeAction() { html$k.addClass('animate-down'); setTimeout(function () { Controller.back(); }, 200); } /** * Создать компонент * @param {string} name - имя компонента * @returns {void} */ function create$b(name) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var comp = new Component$2(name, params); body$1.empty().append(comp.render()); listener$i.send('open', { name: name, body: comp.render(), params: params }); last$2 = name; Controller.toggle('settings_component'); Activity.mixState('settings=' + name); } /** * Обновить открытый компонент * @returns {void} */ function update$f() { var selects = body$1.find('.selector'); var lastinx = selects.index(body$1.find('.selector.focus')); create$b(last$2, { last_index: lastinx }); } /** * Рендер * @param {boolean} js - вернуть DOM-элемент или jQuery объект * @returns {HTMLElement|jQuery} - DOM-элемент или jQuery объект */ function render$i(js) { return js ? html$k[0] : html$k; } var Settings = { listener: listener$i, init: init$1f, render: render$i, update: update$f, create: create$b, main: function main() { return _main; } }; var already_requested = false; var last_time_requested = 0; var personal_codes = {}; function init$1e() { Params.trigger('parental_control', false); Params.select('parental_control_time', { 'always': '#{settings_parental_control_param_time_always}', 'once': '#{settings_parental_control_param_time_once}', '10': '#{settings_parental_control_param_time_10}', '20': '#{settings_parental_control_param_time_20}', '30': '#{settings_parental_control_param_time_30}', '60': '#{settings_parental_control_param_time_60}', '120': '#{settings_parental_control_param_time_120}' }, 'once'); Template.add('settings_parental_control', "
    \n
    \n
    #{title_parental_control}
    \n
    \n
    \n
    \n
    \n
    #{settings_parental_control_change_pin}
    \n
    \n
    \n
    #{settings_parental_control_demand_title}
    \n
    \n
    #{settings_parental_control_demand_descr}
    \n
    \n
    \n
    #{settings_parental_control_where}
    \n
    \n
    \n
    \n
    "); Settings.listener.follow('open', function (e) { if (e.name == 'parental_control') { var toggle = e.body.find('.parental-control-toggle'); var change = e.body.find('.parental-control-change'); var other = e.body.find('.parental-control-other'); var active; // Нельзя изменить код если детский профиль if (permit.child) change.remove(); var updateStatus = function updateStatus() { toggle.find('.settings-param__value').text(Lang.translate(enabled$1() ? 'settings_parental_control_enabled' : 'settings_parental_control_disabled')); other.toggleClass('hide', !Boolean(enabled$1())); }; var drawPersonalList = function drawPersonalList() { var enabled = Storage.get('parental_control_personal', '[]'); var list = e.body.find('.parental-control-personal-list'); var item = function item(name, data) { var line = $("
    \n
    ".concat(Lang.translate(data.title), "
    \n
    \n
    ")); if (enabled.indexOf(name) >= 0) line.addClass('selectbox-item--checked'); line.on('hover:enter', function () { active = Controller.enabled().name; query(function () { enabled = Storage.get('parental_control_personal', '[]'); if (enabled.indexOf(name) >= 0) { Arrays.remove(enabled, name); Storage.set('parental_control_personal', enabled); line.removeClass('selectbox-item--checked'); } else { Storage.add('parental_control_personal', name); line.addClass('selectbox-item--checked'); } Controller.toggle(active); }, function () { Controller.toggle(active); }); }); list.append(line); }; for (var name in personal_codes) { item(name, personal_codes[name]); } Params.listener.send('update_scroll'); }; toggle.on('hover:enter', function () { active = Controller.enabled().name; if (enabled$1()) { request$1(function () { Storage.set('parental_control', false); updateStatus(); Controller.toggle(active); }, function () { Controller.toggle(active); }); } else if (!Storage.value('parental_control_pin')) { set$4(function (code) { if (code) { Storage.set('parental_control_pin', code); Storage.set('parental_control', true); updateStatus(); } }); } else { Storage.set('parental_control', true); updateStatus(); } }); change.on('hover:enter', function () { active = Controller.enabled().name; if (Storage.value('parental_control_pin')) { request$1(function () { set$4(function (code) { if (code) { Storage.set('parental_control_pin', code); } Controller.toggle(active); }); }, function () { Controller.toggle(active); }); } }); updateStatus(); drawPersonalList(); } }); add$d('bookmarks', { title: 'settings_input_links' }); } /** * Запрос на установку PIN-кода * @param {function} call - вызов */ function set$4(call) { var active = Controller.enabled().name; pin(Lang.translate('parental_control_input_new_code'), function (code_one) { if (code_one) { pin(Lang.translate('parental_control_confirm_new_code'), function (code_two) { if (code_one == code_two) { Controller.toggle(active); call(code_two); } else { Controller.toggle(active); Noty.show(Lang.translate('parental_control_no_match_code')); call(); } }); } else { Controller.toggle(active); call(); } }); } /** * Показать экран ввода пина * @param {string} title - заголовок * @param {function} call - вызов */ function pin(title, call) { var input = ''; var html_layer = $("
    \n
    \n
    ".concat(title, "
    \n
    \n
    ")); var html_code = $("
    \n
    \n
    \n
    \n
    \n
    "); var drawPin = function drawPin() { var div = html_code.find('div').removeClass('fill'); for (var i = 0; i < 4; i++) { if (input[i]) div.eq(i).addClass('fill'); } }; var removeNum = function removeNum() { input = input.slice(0, Math.max(0, input.length - 1)); drawPin(); if (!input) callClose(); }; var callClose = function callClose() { html_layer.remove(); call(input); }; if (Platform.tv()) { var html_body = $("
    \n
    \n
    \n ".concat(Lang.translate('pincode_use_remote'), "\n
    \n
    \n ").concat(Lang.translate('pincode_use_toggle'), "\n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n \n \n \n \n \n \n
    \n
    \n
    1
    \n
    \n
    4
    \n
    0
    \n
    2
    \n
    \n
    3
    \n
    \n
    \n\n
    \n
    \n \n \n \n \n \n
    \n
    ").concat(Lang.translate('settings_remove'), "
    \n
    \n
    \n
    \n
    ")); html_body.find('.pincode__left').prepend(html_code); html_layer.find('.pincode__container').append(html_body); var layout_active = 0; var layout_keys = [[1, 2, 3, 4, '0'], [5, 6, 7, 8, 9]]; var drawLayout = function drawLayout() { var keys = layout_keys[layout_active]; html_body.find('.pincode-remote__num-top span').text(keys[0]); html_body.find('.pincode-remote__num-right span').text(keys[1]); html_body.find('.pincode-remote__num-bottom span').text(keys[2]); html_body.find('.pincode-remote__num-left span').text(keys[3]); html_body.find('.pincode-remote__num-center span').text(keys[4]); }; var animate = function animate() { var elem = html_body.find('.pincode-remote').removeClass('push'); setTimeout(function () { elem.addClass('push'); }, 60); }; var writeNum = function writeNum(key) { input = input + layout_keys[layout_active][key]; drawPin(); if (input.length == 4) callClose();else animate(); }; drawLayout(); Controller.add('parental_controll', { toggle: function toggle() { Controller.clear(); }, "long": function long() { layout_active++; if (layout_active >= layout_keys.length) layout_active = 0; drawLayout(); }, enter: function enter() { writeNum(4); }, left: function left() { writeNum(3); }, up: function up() { writeNum(0); }, down: function down() { writeNum(2); }, right: function right() { writeNum(1); }, back: function back() { removeNum(); } }); Lampa.Controller.toggle('parental_controll'); } else { html_layer.addClass('mobile'); var html_keyboard = $("
    \n
    1
    \n
    2
    \n
    3
    \n
    4
    \n
    5
    \n
    6
    \n
    7
    \n
    8
    \n
    9
    \n
    0
    \n
    \n
    \n \n \n \n \n
    \n
    "); html_keyboard.find('[data-key]').on('hover:enter', function () { input = input + $(this).data('key'); drawPin(); if (input.length == 4) callClose(); }); html_keyboard.find('.remove').on('hover:enter', removeNum); html_layer.find('.pincode__container').append(html_code).append(html_keyboard); Controller.add('parental_controll', { toggle: function toggle() { Controller.collectionSet(html_keyboard); Controller.collectionFocus(false, html_keyboard); }, back: function back() { input = ''; call(''); callClose(); }, up: function up() { Navigator.move('up'); }, down: function down() { Navigator.move('down'); }, left: function left() { Navigator.move('left'); }, right: function right() { Navigator.move('right'); } }); Lampa.Controller.toggle('parental_controll'); } if (Platform.mouse()) { html_layer.prepend(HeadBackward('')); } $('body').append(html_layer); setTimeout(function () { html_layer.addClass('animate'); }, 100); } /** * Устаревший метод, но все еще используется, * если пароль состоит не из 4 цифр * @param {string} title * @param {function} call */ function pinFree(title, call) { Input.edit({ free: true, title: title, nosave: true, value: '', layout: 'nums', keyboard: 'lampa', password: true }, call); } /** * Запросить PIN-код для настроек * @param {string} title - заголовок * @param {function} call - вызов */ function request$1(call, error) { if (enabled$1()) { var called = getPinCode().length == 4 ? pin : pinFree; called(Lang.translate('parental_control_input_code'), function (code) { if (code == getPinCode()) { call(); } else if (code) { Noty.show(Lang.translate('parental_control_input_error')); if (error) error(); } else if (error) error(); }); } else { call(); } } /** * Запросить PIN-код (Обший) * @param {function} call - если pin верный * @param {function} error - если нет * @param {boolean} save_controller - переключить на прошлый контроллер */ function query(call, error, save_controller) { if (enabled$1()) { var type = Storage.field('parental_control_time'); var active = Controller.enabled().name; if (type !== 'always') { if (type == 'once') { if (already_requested) return call(); } else { if (last_time_requested + 1000 * 60 * parseInt(type) > Date.now()) return call(); } } request$1(function () { already_requested = true; last_time_requested = Date.now(); if (save_controller) Controller.toggle(active); call(); }, function () { if (save_controller) Controller.toggle(active); if (error) error(); }); } else { call(); } } /** * Запросить PIN-код (Персональный) * @param {string} name - название модуля * @param {function} call - если pin верный * @param {function} error - если нет * @param {boolean} save_controller - переключить на прошлый контроллер */ function personal(name, call, error, save_controller) { var enabled = Storage.get('parental_control_personal', '[]'); if (enabled.indexOf(name) !== -1) query(call, error, save_controller);else call(); } /** * Запрос на установку PIN-кода, перед этим нужно проверить через enabled() * @param {function} call - callback */ function install$3(call) { set$4(function (code) { if (code) { Storage.set('parental_control_pin', code); Storage.set('parental_control', true); } call(Boolean(code)); }); } /** * Добавить персональный PIN-код для запроса * @param {string} name - название модуля * @param {object} data - данные */ function add$d(name, data) { Arrays.extend(data, { title: Lang.translate('player_unknown'), "default": false }); if (data["default"]) Storage.add('parental_control_personal', name); personal_codes[name] = data; } /** * Проверить, установлен ли контроль * @returns bollean */ function enabled$1() { return Storage.field('parental_control') || permit.child; } /** * Получить PIN-код * @returns string */ function getPinCode() { return permit.child ? permit.profile.pincode || '0000' : Storage.value('parental_control_pin'); } var ParentalControl = { init: init$1e, query: query, personal: personal, enabled: enabled$1, install: install$3, add: add$d }; var listener$h = Subscribe$2(); var status$2 = { active: false, channel: false, select: false, program: false }; var numpad = { timer: false, prev: '' }; function init$1d() { Keypad.listener.follow('keydown', function (e) { if (!playning()) return; PlayerPanel.rewind(); //PG- if (e.code === 428 || e.code === 34 || e.code === 4 || e.code === 65) { prevChannel(); playDelay(); } //PG+ if (e.code === 427 || e.code === 33 || e.code === 5 || e.code === 68) { nextChannel(); playDelay(); } var digid = 0; if (e.code >= 48 && e.code <= 57) digid = e.code - 48;else if (e.code >= 96 && e.code <= 105) digid = e.code - 96; if (digid) { clearTimeout(numpad.timer); numpad.prev += digid; toChannel(parseInt(numpad.prev)); numpad.timer = setTimeout(function () { play$3(); numpad.prev = ''; }, 2000); } }); Player.listener.follow('destroy', destroy$o); } function start$a(object) { status$2.position_view = object.position; status$2.position_channel = object.position; status$2.active = object; status$2.channel = channel$1(); listener$h.send('channel', { channel: status$2.channel, dir: 0, position: status$2.position_view }); if (status$2.active.onPlay) status$2.active.onPlay(status$2.channel); listener$h.send('play', { channel: status$2.channel, position: status$2.position_view }); } function playning() { return status$2.active; } function channel$1(position) { status$2.select = status$2.active.onGetChannel(position || status$2.position_view); return status$2.select; } function play$3() { if (status$2.select !== status$2.channel) { status$2.channel = status$2.select; status$2.position_channel = status$2.position_view; if (status$2.active.onPlay) status$2.active.onPlay(status$2.channel); listener$h.send('play', { channel: status$2.channel, position: status$2.position_view }); } } function playDelay() { clearTimeout(status$2.timer); status$2.timer = setTimeout(play$3, 2000); } function reset$1() { status$2.position_view = status$2.position_channel; moveChannel(0); } function programReady(data) { setTimeout(function () { if (status$2.select == data.channel) { status$2.program = data; status$2.position_program = data.position; listener$h.send('draw-program', { dir: 0 }); } }, 10); } function select$3() { return status$2.select; } function moveChannel(dir) { channel$1(status$2.position_view); listener$h.send('channel', { channel: status$2.select, dir: dir, position: status$2.position_view }); } function nextChannel() { if (status$2.position_view + 1 < status$2.active.total) { status$2.position_view++; moveChannel(1); } } function prevChannel() { if (status$2.position_view - 1 >= 0) { status$2.position_view--; moveChannel(-1); } } function toChannel(num) { num = Math.max(1, num); if (num <= status$2.active.total) { status$2.position_view = num - 1; moveChannel(1); } } function redrawChannel() { moveChannel(0); } function moveProgram(dir) { if (status$2.program) { status$2.position_program += dir; status$2.position_program = Math.max(0, Math.min(status$2.program.total, status$2.position_program)); listener$h.send('draw-program', { dir: dir }); } } function nextProgram() { moveProgram(1); } function prevProgram() { moveProgram(-1); } function drawProgram(container) { status$2.active.onGetProgram(status$2.select, status$2.position_program, container); } function playlistProgram() { if (status$2.active.onPlaylistProgram) status$2.active.onPlaylistProgram(status$2.select, status$2.position_program); } function openMenu() { if (status$2.active.onMenu) status$2.active.onMenu(status$2.select, status$2.position_program);else if (status$2.active.onPlaylistProgram) status$2.active.onPlaylistProgram(status$2.select, status$2.position_program); } function destroy$o() { clearTimeout(status$2.timer); clearTimeout(numpad.timer); status$2 = { active: false, channel: false, select: false, program: false }; } function locked(data, call) { var name = Controller.enabled().name; if (data.locked) { ParentalControl.query(call, function () { Controller.toggle(name); }); } else call(); } var PlayerIPTV = { listener: listener$h, init: init$1d, start: start$a, playning: playning, channel: channel$1, programReady: programReady, reset: reset$1, play: play$3, select: select$3, nextChannel: nextChannel, prevChannel: prevChannel, prevProgram: prevProgram, nextProgram: nextProgram, drawProgram: drawProgram, playlistProgram: playlistProgram, openMenu: openMenu, redrawChannel: redrawChannel, locked: locked, destroy: destroy$o }; /** * События для компонентов * Компоненты - классы с методами onEvent и onlyEvent * onEvent - событие, которое вызывается у всех компонентов * onlyEvent - событие, которое вызывается только у одного компонента * Если несколько компонентов реализуют onlyEvent, то будет вызван только первый */ var Emit = /*#__PURE__*/function () { function Emit() { _classCallCheck(this, Emit); this.components = []; } /** * Добавляет компонент * @param {class|object} Module - класс или объект компонента * @param {number|null} position - позиция в массиве компонентов, по умолчанию в конец * @return {void} */ _createClass(Emit, [{ key: "use", value: function use(Module) { var position = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var instance = typeof Module === 'function' ? new Module(this) : Module; if (this.components.includes(instance)) return; if (position === null) { this.components.push(instance); } else { this.components.splice(position, 0, instance); } } /** * Удаляет компонент * @param {class|object} Module - класс или объект компонента * @return {void} */ }, { key: "unuse", value: function unuse(Module) { this.components = this.components.filter(function (c) { return c !== Module; }); } /** * Проверяет наличие компонента * @param {class|object} Module - класс или объект компонента * @return {boolean} */ }, { key: "has", value: function has(Module) { return this.components.some(function (c) { return c === Module || typeof Module === 'function' && c instanceof Module; }); } /** * Вызывает событие у компонентов * @param {string} event - имя события * @param {...any} args - аргументы события * @return {void} */ }, { key: "emit", value: function emit(event) { var _this = this; for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } var name = event.charAt(0).toUpperCase() + event.slice(1); var only = false; this.components.forEach(function (c) { var handler = c["only".concat(name)]; if (typeof handler === 'function') { only = handler; } }); if (only) { var _only; return (_only = only).call.apply(_only, [this].concat(args)); } this.components.forEach(function (c) { var handler = c["on".concat(name)]; if (typeof handler === 'function') { handler.call.apply(handler, [_this].concat(args)); } }); } }]); return Emit; }(); /** * Для работы с битовыми масками * @example * import Mask from '../utils/mask' * import Arrays from '../utils/arrays' * const Helper = new Mask(Arrays.getKeys(Map)) * // где Map - объект с модулями * // например: {Mark:..., Watchlist:..., Favorite:...} * // теперь можно использовать Helper для работы с масками * // например: * // Helper.MASK.Mark - битовая маска для модуля Mark * // Helper.MASK.Favorite - битовая маска для модуля Favorite * // Helper.MASK.all - битовая маска для всех модулей * // Helper.MASK.none - битовая маска для отсутствия модулей * // Helper.only('Mark','Favorite') - битовая маска только для модулей Mark и Favorite * // Helper.add(mask,'Watchlist') - добавляет модуль Watchlist к маске mask * // Helper.remove(mask,'Mark') - удаляет модуль Mark из маски mask * // Helper.toggle(mask,'Favorite') - переключает наличие модуля Favorite в маске mask * // Helper.except('Mark') - битовая маска для всех модулей кроме Mark * // Helper.has(mask,'Mark') - проверяет наличие модуля Mark в маске mask * // Helper.getNames(mask) - возвращает массив названий модулей, присутствующих в маске mask * // Helper.toObject(mask) - возвращает объект с названиями модулей и булевыми значениями их наличия в маске mask * // Helper.describe(mask) - возвращает строковое описание модулей, присутствующих в маске mask */ var Mask = /*#__PURE__*/function () { function Mask(moduleNames) { var _this = this; _classCallCheck(this, Mask); this.moduleNames = moduleNames; this.nameToBit = {}; this.bitToName = {}; this.MASK = {}; moduleNames.forEach(function (name, index) { var bit = 1 << index; _this.nameToBit[name] = bit; _this.bitToName[bit] = name; _this.MASK[name] = bit; }); this.MASK.all = (1 << moduleNames.length) - 1; this.MASK.none = 0; } _createClass(Mask, [{ key: "only", value: function only() { var _this2 = this; for (var _len = arguments.length, names = new Array(_len), _key = 0; _key < _len; _key++) { names[_key] = arguments[_key]; } return names.reduce(function (mask, name) { return mask | _this2._bit(name); }, 0); } }, { key: "add", value: function add(mask) { for (var _len2 = arguments.length, names = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { names[_key2 - 1] = arguments[_key2]; } return mask | this.only.apply(this, names); } }, { key: "remove", value: function remove(mask) { var _this3 = this; for (var _len3 = arguments.length, names = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { names[_key3 - 1] = arguments[_key3]; } return names.reduce(function (acc, name) { return acc & ~_this3._bit(name); }, mask); } }, { key: "toggle", value: function toggle(mask) { var _this4 = this; for (var _len4 = arguments.length, names = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { names[_key4 - 1] = arguments[_key4]; } return names.reduce(function (acc, name) { return acc ^ _this4._bit(name); }, mask); } }, { key: "except", value: function except() { for (var _len5 = arguments.length, names = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { names[_key5] = arguments[_key5]; } return this.remove.apply(this, [this.MASK.all].concat(names)); } }, { key: "has", value: function has(mask, name) { return (mask & this._bit(name)) !== 0; } }, { key: "getNames", value: function getNames(mask) { var _this5 = this; return this.moduleNames.filter(function (name) { return _this5.has(mask, name); }); } }, { key: "toObject", value: function toObject(mask) { var _this6 = this; var obj = {}; this.moduleNames.forEach(function (name) { obj[name] = _this6.has(mask, name); }); return obj; } }, { key: "describe", value: function describe(mask) { var names = this.getNames(mask); return names.length ? names.join(', ') : '(none)'; } }, { key: "_bit", value: function _bit(name) { if (!(name in this.nameToBit)) throw new Error("Unknown module name: ".concat(name)); return this.nameToBit[name]; } }]); return Mask; }(); /** * Конструктор модуля * @param {*} Map - карта модулей * @returns {Class} - класс модуля */ function Constructor() { var Map = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var Helper = new Mask(Arrays.getKeys(Map)); return /*#__PURE__*/function (_Emit) { _inherits(Contrucor, _Emit); var _super = _createSuper(Contrucor); function Contrucor(data) { var _this; _classCallCheck(this, Contrucor); _this = _super.call(this, data); Arrays.extend(data, { params: {} }); _this.data = data; _this.params = data.params; var module = typeof _this.params.module !== 'undefined' ? _this.params.module : Helper.MASK.none; Helper.getNames(module).map(function (name) { return Map[name]; }).forEach(function (mod) { return _this.use(mod); }); _this.emit('init'); return _this; } _createClass(Contrucor, [{ key: "toggle", value: function toggle() { this.emit('toggle'); } }, { key: "create", value: function create() { this.html = document.createElement('div'); this.emit('create'); } }, { key: "render", value: function render(js) { return js ? this.html : $(this.html); } }, { key: "destroy", value: function destroy() { this.html.remove(); this.emit('destroy'); } }]); return Contrucor; }(Emit); } var Row = /*#__PURE__*/function (_Constructor) { _inherits(Row, _Constructor); var _super = _createSuper(Row); function Row() { _classCallCheck(this, Row); return _super.apply(this, arguments); } return Row; }(Constructor({})); var html$j; var scroll$2; var active$7 = 0; var items$2 = []; var listener$g = Subscribe$2(); function init$1c() { html$j = Template.js('player_footer'); scroll$2 = new Scroll({ nopadding: true, over: true }); scroll$2.onWheel = function (step) { if (step > 0) down();else up(); }; html$j.find('.player-footer__body').append(scroll$2.render(true)); Controller.add('player_footer', { toggle: function toggle() { items$2[active$7].toggle(); open$7(); }, up: close$8, right: function right() { Navigator.move('right'); }, left: function left() { Navigator.move('left'); }, back: close$8 }); Player.listener.follow('start', function (data) { if (!data.iptv) { if (data.card) appendAbout(data.card);else { Activity.active().movie && appendAbout(Activity.active().movie); } } }); Player.listener.follow('destroy', destroy$n); } function open$7() { html$j.addClass('open'); listener$g.send('open'); } function close$8() { html$j.removeClass('open'); listener$g.send('close'); } function appendRow(element) { var row = new Row({}); row.use({ onCreate: function onCreate() { this.html.append(element); this.html.addClass('player-footer__row'); }, onToggle: function onToggle() { var _this = this; Controller.add('player_footer_element', { toggle: function toggle() { Controller.collectionSet(_this.html); Controller.collectionFocus(false, _this.html); }, up: this.emit.bind(this, 'up'), down: this.emit.bind(this, 'down'), right: function right() { Navigator.move('right'); }, left: function left() { Navigator.move('left'); }, back: this.emit.bind(this, 'back') }); Controller.toggle('player_footer_element'); } }); appendClass(row); return row; } function removeRow(row) { var index = items$2.indexOf(row); if (index !== -1) { items$2.splice(index, 1); row.destroy(); } } function up() { active$7--; if (active$7 < 0) { active$7 = 0; close$8(); } else { items$2[active$7].toggle(); scroll$2.update(items$2[active$7].render(true)); } } function down() { active$7++; active$7 = Math.min(active$7, items$2.length - 1); scroll$2.update(items$2[active$7].render(true)); items$2[active$7].toggle(); } function appendClass(classElement) { classElement.use({ onUp: up, onDown: down, onToggle: function onToggle() { scroll$2.render(true).style.height = this.render(true).offsetHeight; scroll$2.update(items$2[active$7].render(true)); }, onEnter: function onEnter() { close$8(); if ($('.modal').length) Modal.close(); Lampa.Player.close(); }, onBack: close$8 }); classElement.create(); scroll$2.prepend(classElement.render(true)); Arrays.insert(items$2, 0, classElement); } function appendAbout(card) { var card_html = Template.js('player_footer_card'); var tags = []; var relise = (card.release_date || card.first_air_date || '') + ''; var year = relise ? relise.slice(0, 4) : ''; if (year) tags.push(year); if (card.genres && Arrays.isArray(card.genres)) { tags.push(card.genres.map(function (a) { return Utils$1.capitalizeFirstLetter(a.name); }).join(', ')); } card_html.find('.player-footer-card__title').text(card.name || card.title); card_html.find('.player-footer-card__tags').text(tags.length ? tags.join(' - ') : '---'); var text = card_html.find('.player-footer-card__text'); if (card.overview) text.text(card.overview);else { card_html.addClass('notext'); for (var i = 0; i < 3; i++) { text.append(document.createElement('div')); } } Utils$1.imgLoad(card_html.find('img'), card.poster_path ? Api.img(card.poster_path, 'w200') : './img/img_broken.svg'); appendRow(card_html); } function available() { return items$2.length; } function destroy$n() { Arrays.destroy(items$2); active$7 = 0; scroll$2.reset(); items$2 = []; } function render$h() { return html$j; } var PlayerFooter = { init: init$1c, listener: listener$g, appendAbout: appendAbout, appendRow: appendRow, removeRow: removeRow, render: render$h, destroy: destroy$n, available: available }; var listener$f = Subscribe$2(); var current = ''; var playlist$1 = []; var _position = 0; function init$1b() { PlayerPanel.listener.follow('playlist', show$c); PlayerPanel.listener.follow('prev', prev); PlayerPanel.listener.follow('next', next); Player.listener.follow('start', function (data) { url$4(data.url); if (data.playlist && data.playlist.length) set$3(data.playlist);else set$3(get$e()); }); Player.listener.follow('destroy', destroy$m); PlayerVideo.listener.follow('ended', function (e) { if (Storage.field('playlist_next') && !Select.opened()) next(); }); } /** * Показать плейлист */ function show$c() { if (!playlist$1.length) return; active$6(); var enabled = Controller.enabled(); Select.show({ title: Lang.translate('player_playlist'), items: playlist$1, onSelect: function onSelect(a) { Controller.toggle(enabled.name); listener$f.send('select', { playlist: playlist$1, item: a, position: _position }); }, onBack: function onBack() { Controller.toggle(enabled.name); } }); } /** * Найти текущий элемент в плейлисте и установить его позицию */ function active$6() { playlist$1.forEach(function (p) { return p.selected = false; }); var find = playlist$1.find(function (l) { return l.url == current; }); var data = Player.playdata(); // Если текущий урл не найден в плейлисте, то ищем по урлу из плеера if (!find) find = playlist$1.find(function (l) { return l.url == data.url; }); // Пробуем найти по эпизоду и сезону, если есть if (!find) find = playlist$1.find(function (l) { return l.episode == data.episode && l.season == data.season; }); if (find) { _position = playlist$1.indexOf(find); find.selected = true; } } /** * Назад */ function prev() { active$6(); if (_position > 0) { listener$f.send('select', { playlist: playlist$1, position: _position - 1, item: playlist$1[_position - 1] }); } } /** * Далее */ function next() { if (canNext()) { listener$f.send('select', { playlist: playlist$1, position: _position + 1, item: playlist$1[_position + 1] }); } } /** * Можно ли далее * @returns {boolean} */ function canNext() { active$6(); return _position < playlist$1.length - 1; } /** * Установить плейлист * @param {[{title:string, url:string, thumbnail:string}]} p */ function set$3(p) { playlist$1 = p; active$6(); listener$f.send('set', { playlist: playlist$1, position: _position }); } /** * Получить список * @returns {[{title:string, url:string, thumbnail:string}]} */ function get$e() { return playlist$1; } /** * Установить текуший урл * @param {string} u */ function url$4(u) { current = u; } /** * Очистить плейлист */ function destroy$m() { current = ''; playlist$1 = []; _position = 0; } var PlayerPlaylist = { init: init$1b, listener: listener$f, active: active$6, show: show$c, url: url$4, get: get$e, set: set$3, prev: prev, next: next, canNext: canNext, position: function position() { return _position; }, destroy: destroy$m }; var listener$e = Subscribe$2(); var segments = { ad: [], skip: [] }; var origin = { ad: [], skip: [] }; var ref_duration = 0; var skip_preview_lead = 5; function init$1a() { Player.listener.follow('start', function (data) { if (data.segments) set$2(data.segments); }); Player.listener.follow('destroy', destroy$l); } function update$e(time) { var skip = get$d(time); if (skip && !skip.segment.viewed) { skip.segment.viewed = true; listener$e.send('skip', skip); } } function parseDurationMs(val) { val = parseFloat(val); if (isNaN(val) || val <= 0) return 0; return val / 1000; } function set$2(new_segments) { origin = { ad: [], skip: [] }; ref_duration = 0; if (typeof new_segments === 'number') { ref_duration = parseDurationMs(new_segments); } else if (typeof new_segments === 'string' && new_segments && !isNaN(parseFloat(new_segments))) { ref_duration = parseDurationMs(new_segments); } else if (Arrays.isObject(new_segments)) { for (var i in new_segments) { if (i == 'duration_ms') { ref_duration = parseDurationMs(new_segments[i]); } else if (Arrays.isArray(new_segments[i])) { origin[i] = new_segments[i].map(function (seg) { return Object.assign({}, seg); }); } } } segments = clone$1(origin); listener$e.send('set', segments); } function clone$1(source) { var result = {}; for (var i in source) { result[i] = source[i].map(function (seg) { return Object.assign({}, seg); }); } return result; } function skipHeadSegment(source) { if (!source.skip || !source.skip.length) return null; var head = null; for (var j = 0; j < source.skip.length; j++) { var seg = source.skip[j]; if (!head || (seg.start || 0) < (head.start || 0)) head = seg; } return head; } function introSkipSegment(source) { if (!source.skip || !source.skip.length) return null; var head = null; var limit = ref_duration > 0 ? ref_duration * 0.45 : Infinity; for (var j = 0; j < source.skip.length; j++) { var seg = source.skip[j]; var start = seg.start || 0; // Логотип/бампер в первые секунды — не заставка серии. if (start < 30) continue; if (start > limit) continue; if (!head || start > (head.start || 0)) head = seg; } return head || skipHeadSegment(source); } function tailSkipSegment(source) { if (!source.skip || !source.skip.length) return null; var tail = null; var limit = ref_duration > 0 ? ref_duration * 0.55 : Infinity; for (var j = 0; j < source.skip.length; j++) { var seg = source.skip[j]; var start = seg.start || 0; // Титры у конца эталона — не бампер/промо с 0 с. if (start < 30) continue; if (ref_duration > 0 && start < limit) continue; if (!tail || (seg.end || 0) > (tail.end || 0)) tail = seg; } return tail; } function mapSegment(seg, duration, is_tail, credit_ref_len, tail_start) { var start = seg.start || 0; var end = seg.end || 0; if (!ref_duration) return Object.assign({}, seg); // Почти эталон — метки. if (Math.abs(duration - ref_duration) <= 2) { return Object.assign({}, seg); } var slack = duration - ref_duration; // Короче эталона — в начале урезано, якорь с конца уводит заставку раньше. if (slack < 0) { return Object.assign({}, seg); } if (is_tail) { var credit_len = Math.max(1, end - start); var mapped_start = duration - credit_len; // Лишняя длина чаще в начале — титры фикс. длины у конца файла. if (credit_ref_len > 0 && slack < credit_ref_len) { mapped_start -= Math.floor(slack * 0.3); } return { start: Math.max(0, Math.min(duration - 1, mapped_start)), end: duration }; } // Бампер 0–30 с не сдвигаем. if (start < 30) return Object.assign({}, seg); // Малый slack (+3…20 с) — часть offset в титрах, заставка ближе к title card. if (slack <= 20 && tail_start > start) { var width = end - start || 1; var base = duration - (ref_duration - start); var booster = slack <= 10 ? Math.floor(slack * 2) : Math.floor(slack * 0.75); var guide = start + Math.floor(start / 45) + booster; var mapped = Math.max(base, guide); var _copy = { start: mapped, end: mapped + width }; _copy.start = Math.max(0, Math.min(duration, _copy.start)); _copy.end = Math.max(_copy.start + 1, Math.min(duration, _copy.end)); return _copy; } var copy = { start: duration - (ref_duration - start), end: duration - (ref_duration - end) }; // Средний slack — часть offset в титрах, заставка ближе к контенту. if (credit_ref_len > 0 && slack < credit_ref_len) { var _width = end - start || 1; var _base = duration - (ref_duration - start); var anchor = start + Math.floor(slack * 0.84); var softened = start + Math.floor(start / 45) + Math.floor(slack * 0.52); if (_base > anchor) { copy.start = anchor; } else { copy.start = _base + Math.floor(slack * 0.2); } if (slack >= 100 && softened > start && softened < copy.start) { copy.start = softened; } copy.end = copy.start + _width; } copy.start = Math.max(0, Math.min(duration, copy.start)); copy.end = Math.max(copy.start + 1, Math.min(duration, copy.end)); return copy; } function keepEarlySkip(seg, list) { var start = seg.start || 0; var end = seg.end || 0; if (start >= 30) return true; // Убираем только дубль заставки: пересечение или «хвост» в зону intro. for (var j = 0; j < list.length; j++) { var other = list[j]; var oStart = other.start || 0; if (oStart < 30) continue; var oEnd = other.end || 0; if (end > oStart && start < oEnd) return false; if (end > oStart + 15) return false; } return true; } function shouldKeepSkipSegment(seg, list, anchor) { var start = seg.start || 0; var end = seg.end || 0; if (!keepEarlySkip(seg, list)) return false; if (end >= ref_duration - 60) return true; if (start >= anchor - 1) return true; // Бампер/лого до заставки — отдельная зона, не дубль intro. if (end <= anchor + 15) return true; return false; } function pruneSkipDuplicates() { if (!segments.skip || !segments.skip.length) return; var intro = introSkipSegment(segments); if (!intro) return; var anchor = intro.start || 0; segments.skip = segments.skip.filter(function (seg) { return shouldKeepSkipSegment(seg, segments.skip, anchor); }); } function skipSegmentsUi() { var skips = [].concat(segments.skip || [], segments.ad || []); if (!skips.length) return []; var intro = introSkipSegment(segments); if (!intro) return skips; var anchor = intro.start || 0; return skips.filter(function (seg) { return shouldKeepSkipSegment(seg, skips, anchor); }); } function scanSkip(time, lead, preview) { var list = skipSegmentsUi(); if (!list.length) return preview ? false : null; var nearest = false; for (var j = 0; j < list.length; j++) { var seg = list[j]; if (seg.skiped) continue; if (time >= seg.start && time <= seg.end) { if (preview) return { type: 'skip', segment: seg, phase: 'inside' }; return { type: 'skip', segment: seg }; } if (preview && time >= seg.start - lead && time < seg.start) { var item = { type: 'skip', segment: seg, phase: 'preview', starts_in: Math.max(1, Math.ceil(seg.start - time)) }; if (!nearest || seg.start < nearest.segment.start) nearest = item; } } return nearest; } function adjust(duration) { duration = parseFloat(duration); var valid = Boolean(duration) && !isNaN(duration); if (!valid || !ref_duration) { segments = clone$1(origin); listener$e.send('set', segments); return; } var tail = tailSkipSegment(origin); var credit_ref_len = tail ? Math.max(0, (tail.end || 0) - (tail.start || 0)) : 0; var tail_start = tail ? tail.start || 0 : 0; var tail_at_ref = tail && Math.abs((tail.end || 0) - ref_duration) <= 15 && (tail.start || 0) >= ref_duration - Math.max(credit_ref_len, 60) - 15; segments = {}; for (var i in origin) { segments[i] = origin[i].map(function (seg) { var is_tail = tail_at_ref && seg === tail; return mapSegment(seg, duration, is_tail, credit_ref_len, tail_start); }); } pruneSkipDuplicates(); listener$e.send('set', segments); } function get$d(time) { var skip = scanSkip(time, skip_preview_lead, false); return skip || false; } function getNear(time, lead) { lead = parseFloat(lead); if (!lead || isNaN(lead)) lead = skip_preview_lead; time = parseFloat(time) || 0; var skip = scanSkip(time, lead, true); if (skip) return skip; for (var i in segments) { if (i == 'skip') continue; if (!segments[i]) continue; for (var j = 0; j < segments[i].length; j++) { var seg = segments[i][j]; if (seg.skiped) continue; if (time >= seg.start && time <= seg.end) { return { type: i, segment: seg, phase: 'inside' }; } if (time >= seg.start - lead && time < seg.start) { return { type: i, segment: seg, phase: 'preview', starts_in: Math.max(1, Math.ceil(seg.start - time)) }; } } } return false; } function all$4() { return segments; } function destroy$l() { segments = { ad: [], skip: [] }; origin = { ad: [], skip: [] }; } var Segments$1 = { init: init$1a, listener: listener$e, update: update$e, set: set$2, adjust: adjust, get: get$d, getNear: getNear, all: all$4, destroy: destroy$l }; /** * Отрисовка сегментов на таймлайне плеера */ function init$19() { PlayerVideo.listener.follow('loadeddata', draw$7); Segments$1.listener.follow('set', draw$7); } function draw$7() { var segments = Segments$1.all(); var timeline = Html.elem('segments').empty(); var video = PlayerVideo.video(); var duration = video && video.duration ? video.duration : 0; for (var name in segments) { for (var a = 0; a < segments[name].length; a++) { var seg = segments[name][a]; var seg_elem = $("
    ")); var r_start = Math.min(duration, seg.start); var r_end = Math.min(duration, seg.end); var start = duration ? r_start / duration * 100 : 0; var length = duration ? (r_end - r_start) / duration * 100 : 0; seg_elem.css({ left: duration ? start + '%' : 0, width: duration ? length + '%' : 0 }); timeline.append(seg_elem); } } } function destroy$k() { Html.elem('segments').empty(); } var Segments = { init: init$19, destroy: destroy$k }; var last_action; /** * Показать настройки плеера */ function show$b() { var speed = Storage.get('player_speed', 'default'); var items = [{ title: Lang.translate('player_video_size'), subtitle: Lang.translate('player_size_' + Storage.get('player_size', 'default') + '_title'), method: 'size' }, { title: Lang.translate('player_video_speed'), subtitle: speed == 'default' ? Lang.translate('player_speed_default_title') : speed, method: 'speed' }, { title: Lang.translate('player_share_title'), subtitle: Lang.translate('player_share_descr'), method: 'share' }, { title: Lang.translate('player_segments_title'), subtitle: Lang.translate('player_segments_descr'), method: 'segments' }, { title: Lang.translate('settings_player_subs'), method: 'subs' }]; if (Storage.field('player_normalization')) { items.push({ title: Lang.translate('player_normalization'), separator: true }); items.push({ title: Lang.translate('player_normalization_power_title'), subtitle: Lang.translate('player_normalization_step_' + Storage.get('player_normalization_power', 'hight')), method: 'normalization_power' }); items.push({ title: Lang.translate('player_normalization_smooth_title'), subtitle: Lang.translate('player_normalization_step_' + Storage.get('player_normalization_smooth', 'medium')), method: 'normalization_smooth' }); items.push({ title: Lang.translate('player_normalization_type_title'), subtitle: Lang.translate('player_normalization_type_' + Storage.get('player_normalization_type', 'all')), method: 'normalization_type' }); } if (last_action) { items.find(function (a) { return a.method == last_action; }).selected = true; } Select.show({ title: Lang.translate('title_settings'), items: items, nomark: true, onSelect: function onSelect(a) { last_action = a.method; if (a.method == 'size') selectSize(); if (a.method == 'speed') selectSpeed(); if (a.method == 'normalization_power') selectNormalizationStep('power', 'hight'); if (a.method == 'normalization_smooth') selectNormalizationStep('smooth', 'medium'); if (a.method == 'normalization_type') selectNormalizationType(); if (a.method == 'segments') selectSegments(); if (a.method == 'subs') selectSubs(); if (a.method == 'share') { Controller.toggle(Platform.screen('mobile') ? 'player' : 'player_panel'); listener$j.send('share', {}); } }, onBack: function onBack() { Controller.toggle(Platform.screen('mobile') ? 'player' : 'player_panel'); } }); } function selectSubs() { var items = [{ title: Lang.translate('settings_player_subs_size'), subtitle: Lang.translate('settings_player_subs_size_descr'), name: 'subtitles_size' }, { title: Lang.translate('settings_player_subs_stroke_use'), subtitle: Lang.translate('settings_player_subs_stroke_use_descr'), name: 'subtitles_stroke' }, { title: Lang.translate('settings_player_subs_backdrop_use'), subtitle: Lang.translate('settings_player_subs_backdrop_use_descr'), name: 'subtitles_backdrop' }, { title: Lang.translate('settings_rest_time'), name: 'player_subs_shift_time' }]; Select.show({ title: Lang.translate('settings_player_subs'), items: items, nohide: true, onBack: show$b, onSelect: function onSelect(a) { var subitems = []; if (a.name == 'subtitles_size') { subitems = [{ title: Lang.translate('settings_param_subtitles_size_small'), value: 'small' }, { title: Lang.translate('settings_param_subtitles_size_normal'), value: 'normal' }, { title: Lang.translate('settings_param_subtitles_size_bigger'), value: 'large' }]; } if (a.name == 'subtitles_stroke' || a.name == 'subtitles_backdrop') { subitems = [{ title: Lang.translate('settings_param_yes'), value: 'true' }, { title: Lang.translate('settings_param_no'), value: 'false' }]; } if (a.name == 'player_subs_shift_time') { subitems = [-120, -90, -60, -30, -10, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 10, 30, 60, 90, 120]; subitems = subitems.map(function (i) { return { title: (i > 0 ? '+' : '') + i + ' sec.', value: i, selected: Storage.get('player_subs_shift_time', '0') == i }; }); } else { subitems.forEach(function (i) { i.selected = Storage.field(a.name) + '' == i.value; }); } Select.show({ title: a.title, items: subitems, nohide: true, onBack: selectSubs, onSelect: function onSelect(b) { Storage.set(a.name, b.value); listener$j.send('subs', { type: a.name, value: b.value }); selectSubs(); } }); } }); } function selectSegments() { var items = [{ title: Lang.translate('player_segments_ad_title'), name: 'ad', subtitle: Lang.translate('player_segments_value_' + Storage.get('player_segments_ad', 'auto')) }, { title: Lang.translate('player_segments_skip_title'), name: 'skip', subtitle: Lang.translate('player_segments_value_' + Storage.get('player_segments_skip', 'auto')) }]; Select.show({ title: Lang.translate('player_segments_title'), items: items, nohide: true, onBack: show$b, onSelect: function onSelect(a) { Select.show({ title: a.title, items: [{ title: Lang.translate('player_segments_value_auto'), value: 'auto', selected: Storage.get('player_segments_' + a.name, 'auto') == 'auto' }, { title: Lang.translate('player_segments_value_user'), value: 'user', selected: Storage.get('player_segments_' + a.name, 'auto') == 'user' }, { title: Lang.translate('player_segments_value_none'), value: 'none', selected: Storage.get('player_segments_' + a.name, 'auto') == 'none' }], nohide: true, onBack: selectSegments, onSelect: function onSelect(b) { Storage.set('player_segments_' + a.name, b.value); listener$j.send('segments', { type: a.name, value: b.value }); selectSegments(); } }); } }); } function selectNormalizationType() { var select = Storage.get('player_normalization_type', 'all'); var items = [{ title: Lang.translate('player_normalization_type_all'), value: 'all', selected: select == 'all' }, { title: Lang.translate('player_normalization_type_up'), value: 'up', selected: select == 'up' }, { title: Lang.translate('player_normalization_type_down'), value: 'down', selected: select == 'down' }]; Select.show({ title: Lang.translate('player_normalization_type_title'), items: items, nohide: true, onBack: show$b, onSelect: function onSelect(a) { Storage.set('player_normalization_type', a.value); listener$j.send('normalization', { type: 'type', value: a.value }); show$b(); } }); } function selectNormalizationStep(type, def) { var select = Storage.get('player_normalization_' + type, def); var items = [{ title: Lang.translate('player_normalization_step_none'), value: 'none', selected: select == 'none' }, { title: Lang.translate('player_normalization_step_low'), value: 'low', selected: select == 'low' }, { title: Lang.translate('player_normalization_step_medium'), value: 'medium', selected: select == 'medium' }, { title: Lang.translate('player_normalization_step_hight'), value: 'hight', selected: select == 'hight' }]; Select.show({ title: Lang.translate('player_normalization_' + type + '_title'), items: items, nohide: true, onBack: show$b, onSelect: function onSelect(a) { Storage.set('player_normalization_' + type, a.value); listener$j.send('normalization', { type: type, value: a.value }); show$b(); } }); } function selectSize() { var select = Storage.get('player_size', 'default'); var items = [{ title: Lang.translate('player_size_default_title'), subtitle: Lang.translate('player_size_default_descr'), value: 'default', selected: select == 'default' }, { title: Lang.translate('player_size_cover_title'), subtitle: Lang.translate('player_size_cover_descr'), value: 'cover', selected: select == 'cover' }]; if (Platform.is('orsay') && Storage.field('player') == 'orsay') { items.splice(1, 1); } if (!(Platform.is('tizen') && Storage.field('player') == 'tizen')) { items = items.concat([{ title: Lang.translate('player_size_fill_title'), subtitle: Lang.translate('player_size_fill_descr'), value: 'fill', selected: select == 'fill' }, { title: Lang.translate('player_size_s115_title'), subtitle: Lang.translate('player_size_s115_descr'), value: 's115', selected: select == 's115' }, { title: Lang.translate('player_size_s130_title'), subtitle: Lang.translate('player_size_s130_descr'), value: 's130', selected: select == 's130' }, { title: Lang.translate('player_size_v115_title'), subtitle: Lang.translate('player_size_v115_descr'), value: 'v115', selected: select == 'v115' }, { title: Lang.translate('player_size_v130_title'), subtitle: Lang.translate('player_size_v130_descr'), value: 'v130', selected: select == 'v130' }]); } else { if (select == 's130' || select == 'fill') { items[0].selected = true; } } Select.show({ title: Lang.translate('player_video_size'), items: items, nohide: true, onSelect: function onSelect(a) { listener$j.send('size', { size: a.value }); }, onBack: show$b }); } function selectSpeed() { var select = Storage.get('player_speed', 'default'); var items = [{ title: '0.25', value: '0.25' }, { title: '0.50', value: '0.50' }, { title: '0.75', value: '0.75' }, { title: Lang.translate('player_speed_default_title'), value: 'default' }, { title: '1.25', value: '1.25' }, { title: '1.50', value: '1.50' }, { title: '1.75', value: '1.75' }, { title: '2', value: '2' }]; if (Platform.is('tizen') && Storage.field('player') == 'tizen' || Platform.is('orsay') && Storage.field('player') == 'orsay') { items = [{ title: Lang.translate('player_speed_default_title'), value: 'default', selected: select == 'default' }, { title: '2', subtitle: Platform.is('orsay') && Storage.field('player') == 'orsay' ? Lang.translate('player_speed_two_descr') : '', value: '2' }]; } var any; items.forEach(function (e) { if (e.value == select) { any = true; e.selected = true; } }); if (!any) { Storage.set('player_speed', 'default'); if (items.length == 3) items[0].selected = true;else items[3].selected = true; } Select.show({ title: Lang.translate('player_video_speed'), items: items, nohide: true, onSelect: function onSelect(a) { Storage.set('player_speed', a.value); listener$j.send('speed', { speed: a.value }); show$b(); }, onBack: show$b }); } var PanelSettings = { listener: listener$j, show: show$b }; var channel_active; function playAnimation(elem, anim) { elem.css('animation', 'none'); elem[0].offsetHeight; elem.css('animation', (anim || 'pulse') + ' 0.2s ease'); } function program(data) { if (channel_active) { var prog = channel_active.find('.player-panel-iptv-item__prog'); PlayerIPTV.drawProgram(prog); playAnimation(prog, data.dir > 0 ? 'endless-left' : 'endless-right'); } } function channel(data) { var select = PlayerIPTV.select(); Html.elem('iptv_channel').removeClass('up down'); var active = Html.elem('iptv_channel').find('.active'); Html.elem('iptv_channel').find('> div:not(.active)').remove(); var new_item = $("\n
    \n
    \n \n
    \n
    \n
    ".concat(select.group, "
    \n
    ").concat(select.name, "
    \n
    \n
    ").concat(Lang.translate('loading'), "...
    \n
    \n
    \n
    \n ")); if (select.icons) { select.icons.forEach(function (ic) { new_item.find('.player-panel-iptv-item__name').append($('
    ' + ic + '
    ')); }); } var ico = new_item.find('.player-panel-iptv-item__ico'); var img = ico[0]; img.onload = function () { ico.addClass('loaded'); }; img.onerror = function () { ico.remove(); $('.player-panel-iptv-item__left', new_item).append("\n \n \n \n \n \n \n "); }; if (select.logo) img.src = select.logo;else img.onerror(); new_item.css({ '-webkit-transform': 'translate3d(0,' + (data.dir > 0 ? '100%' : '-100%') + ',0)' }); Html.elem('iptv_channel').append(new_item); channel_active = new_item; playAnimation(Html.elem('iptv_position')); playAnimation(data.dir > 0 ? Html.elem('iptv_arrow_down') : Html.elem('iptv_arrow_up')); setTimeout(function () { new_item.css({ '-webkit-transform': 'translate3d(0,0,0)', opacity: 1 }); if (active.length) active.removeClass('active').css({ '-webkit-transform': 'translate3d(0,' + (data.dir > 0 ? '-100%' : '100%') + ',0)', opacity: 0 }); Html.elem('iptv_position').text((data.position + 1).pad(3)); }, 10); } function settings$1() { var enabled = Controller.enabled().name; var items = []; items.push({ title: Lang.translate('player_tracks'), trigger: Html.elem('tracks'), ghost: Html.elem('tracks').hasClass('hide'), noenter: Html.elem('tracks').hasClass('hide') }); items.push({ title: Lang.translate('player_subs'), trigger: Html.elem('subs'), ghost: Html.elem('subs').hasClass('hide'), noenter: Html.elem('subs').hasClass('hide') }); items.push({ title: Lang.translate('player_quality'), trigger: Html.elem('quality'), ghost: !Option.hasQuality(), noenter: !Option.hasQuality() }); items.push({ title: Lang.translate('settings_main_rest'), trigger: Html.elem('settings') }); Select.show({ title: Lang.translate('title_settings'), items: items, onSelect: function onSelect(a) { Controller.toggle(enabled); a.trigger.trigger('hover:enter'); }, onBack: function onBack() { Controller.toggle(enabled); } }); } var IptvPanel = { channel: channel, program: program, settings: settings$1 }; var skip_button; var skip_line; var skip_current = null; var skip_timer = null; var skip_text_in = ''; var skip_text_btn = ''; var skip_is_end = false; var skip_phase = ''; function init$18() { skip_button = $("
    "); skip_line = skip_button.find('.player-skip__progress-line'); skip_button.on('hover:enter', skipDo); PlayerVideo.listener.follow('timeupdate', function (e) { var near = Segments$1.getNear(e.current || 0); var user_seg = near && Storage.get('player_segments_' + near.type, 'auto') == 'user' && !near.segment.skiped; if (user_seg) { if (near.phase === 'preview') { if (!skip_current || skip_current.segment !== near.segment || skip_phase !== 'preview') preview(near);else previewUpdate(near.starts_in); } else if (near.phase === 'inside') { if (!skip_current || skip_current.segment !== near.segment || skip_phase !== 'active') active$5(near, e.current || 0);else progressUpdate(e.current || 0); } } else if (skip_current) hide$2(); }); Player.listener.follow('destroy', destroy$j); addController$1(); } function addController$1() { Controller.add('player_skip', { toggle: function toggle() { var html = Player.render(); Controller.collectionSet(html); Controller.collectionFocus(skip_button[0], html); }, up: function up() { if (PlayerPanel.visibleStatus()) PlayerPanel.hide();else PlayerPanel.reveal(); }, down: PlayerPanel.toggle, left: Player.toggle, right: Player.toggle, gone: function gone() { if (skip_button) skip_button.removeClass('focus'); }, back: Player.close }); } function toggle$a() { if (!isActive$2()) return; Controller.toggle('player_skip'); } function isActive$2() { return skip_phase === 'active' && Boolean(skip_button) && !skip_button.hasClass('hide'); } function progressSet(value, instant) { if (!skip_line || !skip_line.length) return; var scale = Math.max(0, Math.min(1, value)); // reflow до и после установки, иначе браузер склеит смену класса и transform в один кадр и проиграет transition if (instant) { skip_line.addClass('player-skip__progress-line--instant'); skip_line[0].offsetWidth; } skip_line.css({ '-webkit-transform': 'scaleX(' + scale + ')', 'transform': 'scaleX(' + scale + ')' }); if (instant) { skip_line[0].offsetWidth; skip_line.removeClass('player-skip__progress-line--instant'); } } function progressUpdate(current, instant) { if (skip_phase !== 'active' || !skip_current) return; var seg = skip_current.segment; var length = (seg.end || 0) - (seg.start || 0); progressSet(length > 0 ? ((seg.end || 0) - current) / length : 0, instant); } function draw$6(e) { var v = PlayerVideo.video(); var dur = v ? v.duration || 0 : 0; var end = dur && e.type == 'skip' && (e.segment.start >= dur * 0.7 || e.segment.end >= dur - 15); skip_is_end = Boolean(end); if (end) { skip_text_in = Lang.translate('player_segments_next_in'); skip_text_btn = Lang.translate('player_segments_next'); } else { skip_text_in = Lang.translate('player_segments_skip_in'); skip_text_btn = Lang.translate('player_segments_skip_now'); } } function preview(e) { if (!skip_button) return; clearInterval(skip_timer); skip_current = e; skip_phase = 'preview'; draw$6(e); previewUpdate(e.starts_in); progressSet(0, true); skip_button.removeClass('hide focus').addClass('player-skip--preview'); } function previewUpdate(seconds) { if (!skip_button || skip_phase !== 'preview') return; skip_button.find('.player-skip__text').text(skip_text_in + ' ' + seconds); } function active$5(e, current) { if (!skip_button) return; clearInterval(skip_timer); skip_current = e; skip_phase = 'active'; draw$6(e); skip_button.removeClass('player-skip--preview'); show$a(); progressUpdate(typeof current == 'number' ? current : e.segment.start, true); } function show$a() { if (!skip_button) return; clearInterval(skip_timer); skip_button.find('.player-skip__text').text(skip_text_btn); skip_button.removeClass('hide'); if (Controller.enabled().name == 'player') toggle$a(); } function hide$2() { clearInterval(skip_timer); if (skip_button) skip_button.addClass('hide').removeClass('focus player-skip--preview'); progressSet(0, true); var was = skip_current; skip_current = null; skip_phase = ''; if (was && Controller.enabled().name == 'player_skip') Controller.toggle('player'); } function skipDo() { if (skip_phase !== 'active') return; if (skip_current) { skip_current.segment.skiped = true; if (skip_is_end) { hide$2(); PlayerPlaylist.next(); return; } PlayerVideo.to(Math.min(PlayerVideo.video().duration || skip_current.segment.end, skip_current.segment.end)); } hide$2(); } function render$g() { return skip_button; } function destroy$j() { hide$2(); } var Skip = { init: init$18, render: render$g, hide: hide$2, preview: preview, toggle: toggle$a, previewUpdate: previewUpdate, active: active$5, "do": skipDo, isActive: isActive$2, phase: function phase() { return skip_phase; }, current: function current() { return skip_current; }, button: function button() { return skip_button; }, destroy: destroy$j }; /** * Добавить HTML в панель плеера * @param {string} side - Сторона панели ('left', 'right', 'top', 'bottom') * @param {string} html - HTML-код для добавления */ function push$9(side, html) { Html.render().find('.player-panel__apex-' + side + '-side').prepend(html); } function render$f(side) { return Html.render().find('.player-panel__apex-' + side + '-side'); } function clear$c(side) { Html.render().find('.player-panel__apex-' + side + '-side').empty(); } function destroy$i() { clear$c('left'); clear$c('right'); clear$c('bottom'); clear$c('top'); } var Apex = { push: push$9, render: render$f, clear: clear$c, destroy: destroy$i }; /** * Отрисовка следующего эпизода в плеере */ function init$17() { PlayerPlaylist.listener.follow('set', function (e) { clear$b(); var next = e.playlist[e.position + 1]; if (next) draw$5(next); }); } /** * Отрисовка следующего эпизода * @param {Object} item - Данные следующего эпизода * @param {string} item.title - Название эпизода * @param {string} item.thumbnail - URL миниатюры эпизода * @param {string} item.episode - Номер эпизода */ function draw$5(item) { var content = []; if (item.thumbnail) { var thumbnail = Template.elem('div', { "class": 'player-next__thumbnail', children: [Template.elem('img', { "class": 'player-next__thumbnail-img' })] }); Utils$1.imgLoad(thumbnail.find('img'), item.thumbnail, function () { thumbnail.addClass('loaded'); }); content.push(thumbnail); } var body = Template.elem('div', { "class": 'player-next__body', children: [Template.elem('div', { "class": 'player-next__title', text: Lang.translate('player_segments_next') }), Template.elem('div', { "class": 'player-next__episode-title', text: item.title })] }); content.push(body); var box = Template.elem('div', { "class": 'player-next', children: [Template.elem('div', { "class": 'player-next__episode', children: content })] }); Apex.push('left', box); } function clear$b() { Apex.render('left').find('.player-next').remove(); } function destroy$h() { clear$b(); } var Next$2 = { init: init$17, destroy: destroy$h }; var state; var panel_visible = false; var timeline_last = { position: 0, peding: 0 }; var condition = {}; var timer$7 = {}; function init$16() { Html.init(); Option.init(); Segments.init(); Next$2.init(); /** * Отсеживаем состояние, * когда надо показать панель, а когда нет */ state = new StateMachine({ state: 'start', transitions: { start: function start() { clearTimeout(timer$7.hide); clearTimeout(timer$7.rewind); this.dispath('canplay'); }, canplay: function canplay() { if (condition.canplay) this.dispath('visible');else _visible(true); }, visible: function visible() { if (condition.visible) _visible(true);else this.dispath('rewind'); }, rewind: function rewind() { var _this = this; clearTimeout(timer$7.rewind); if (condition.rewind) { _visible(true); timer$7.rewind = setTimeout(function () { condition.rewind = false; _this.dispath('mousemove'); }, 1000); } else { this.dispath('mousemove'); } }, mousemove: function mousemove() { if (condition.mousemove) { _visible(true); } this.dispath('hide'); }, hide: function hide() { clearTimeout(timer$7.hide); timer$7.hide = setTimeout(function () { if (PlayerIPTV.playning()) { PlayerIPTV.reset(); Controller.toggle('player'); } else if (Html.render().hasClass('panel--footer-open')) ; else if (!PlayerVideo.video().paused) _visible(false); }, PlayerIPTV.playning() || Platform.screen('mobile') ? 5000 : 3000); } } }); PlayerPlaylist.listener.follow('set', function (e) { Html.elem('playlist_buttons').toggleClass('hide', Boolean(e.playlist.length == 0)); Html.elem('next').toggleClass('hide', !PlayerPlaylist.canNext()); }); Html.elem('playpause').on('hover:enter', function (e) { listener$j.send('playpause', {}); }); Html.elem('next').on('hover:enter', function (e) { listener$j.send('next', {}); }); Html.elem('prev').on('hover:enter', function (e) { listener$j.send('prev', {}); }); Html.elem('rprev').on('hover:enter', function (e) { listener$j.send('rprev', {}); }); Html.elem('rnext').on('hover:enter', function (e) { listener$j.send('rnext', {}); }); Html.elem('playlist').on('hover:enter', function (e) { listener$j.send('playlist', {}); }); Html.elem('tstart').on('hover:enter', function (e) { listener$j.send('to_start', {}); }); Html.elem('tend').on('hover:enter', function (e) { listener$j.send('to_end', {}); }); Html.elem('fullscreen').on('hover:enter', function (e) { listener$j.send('fullscreen', {}); }); Html.elem('settings').on('hover:enter', PanelSettings.show); Html.render().find('.player-panel__pip,.player-panel__volume').toggleClass('hide', !Boolean(Platform.desktop() || Platform.is('browser') || Platform.is('apple') && !Utils$1.isPWA())); Html.elem('pip').on('hover:enter', function () { listener$j.send('pip', {}); }); Html.elem('rewind_touch').toggleClass('hide', !Platform.screen('mobile')); Html.elem('timeline').on('mousemove', function (e) { if (!Platform.screen('mobile')) listener$j.send('mouse_rewind', { method: 'move', time: Html.elem('time'), percent: percent(e) }); }).on('mouseout', function () { if (!Platform.screen('mobile')) Html.elem('time').addClass('hide'); }).on('click', function (e) { if (DeviceInput.canClick(e.originalEvent) && !Platform.screen('mobile')) listener$j.send('mouse_rewind', { method: 'click', time: Html.elem('time'), percent: percent(e) }); }); if (!Html.elem('volume').hasClass('hide')) { Html.elem('volume').find('.player-panel__volume-range').val(Storage.get('player_volume', '1')).on('input', function () { listener$j.send('change_volume', { volume: $(this).val() }); PlayerVideo.volume($(this).val()); }); } var touch; var touchEnd = function touchEnd(e) { window.removeEventListener('touchend', touchEnd); PlayerVideo.video().rewind = false; listener$j.send('mouse_rewind', { method: 'click', time: Html.elem('time'), percent: touch.to / 100 }); touch = false; }; Html.elem('rewind_touch').on('touchstart', function (e) { var point = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; touch = { now: percent({ clientX: Html.elem('position').width() }) * 100, from: percent(point) * 100 }; touch.move = touch.from; touch.to = touch.from; window.addEventListener('touchend', touchEnd); }).on('touchmove', function (e) { var point = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; if (touch) { touch.move = percent(point) * 100; touch.to = Math.max(0, Math.min(100, touch.now + (touch.move - touch.from))); timeline_last.position = touch.to + '%'; Html.elem('timenow').text(Utils$1.secondsToTime(touch.to / 100 * (PlayerVideo.video().duration || 0))); PlayerVideo.video().rewind = true; rewind$1(); } }); PlayerIPTV.listener.follow('channel', IptvPanel.channel); PlayerIPTV.listener.follow('draw-program', IptvPanel.program); Player.listener.follow('start', function (data) { show$9(); Option.setQualitys(data.quality, data.url); Option.setTracks(data.voiceovers); Option.setTranslates(data.translate); }); Player.listener.follow('destroy', destroy$g); PlayerFooter.listener.follow('open', function () { Html.render().addClass('panel--footer-open'); }); PlayerFooter.listener.follow('close', function () { Html.render().removeClass('panel--footer-open'); Controller.toggle('player_panel'); }); PlayerVideo.listener.follow('timeupdate', function (e) { update$d('time', Utils$1.secondsToTime(e.current | 0, true)); update$d('timenow', Utils$1.secondsToTime(e.current || 0)); update$d('timeend', Utils$1.secondsToTime(e.duration || 0) + ' - ' + Lang.translate('title_left') + ' ' + Utils$1.secondsToTimeHuman(e.duration - e.current || 0)); update$d('position', e.current / e.duration * 100 + '%'); }); PlayerVideo.listener.follow('progress', function (e) { update$d('peding', e.down); }); PlayerVideo.listener.follow('canplay', function () { canplay(); }); PlayerVideo.listener.follow('play', function () { update$d('play'); rewind$1(); }); PlayerVideo.listener.follow('pause', function () { update$d('pause'); }); PlayerVideo.listener.follow('rewind', function () { rewind$1(); }); PlayerVideo.listener.follow('subs', function (e) { Option.setSubtitles(e.subs); }); PlayerVideo.listener.follow('levels', function (e) { Option.setLevels(e.levels, e.current); }); PlayerVideo.listener.follow('translate', function (e) { Option.updateTranslate(e.where, e.translate); }); listener$j.follow('mouse_rewind', function (e) { var vid = PlayerVideo.video(); if (vid && vid.duration) { if (!Platform.screen('mobile')) e.time.removeClass('hide').text(Utils$1.secondsToTime(vid.duration * e.percent)).css('left', e.percent * 100 + '%'); if (e.method == 'click') { PlayerVideo.to(vid.duration * e.percent); } } }); listener$j.follow('playpause', function () { PlayerVideo.playpause(); if (Platform.screen('mobile')) rewind$1(); }); listener$j.follow('size', function (e) { PlayerVideo.size(e.size); Storage.set('player_size', e.size); }); listener$j.follow('speed', function (e) { PlayerVideo.speed(e.speed); Storage.set('player_speed', e.speed); }); listener$j.follow('rprev', function () { PlayerVideo.rewind(false); }); listener$j.follow('rnext', function () { PlayerVideo.rewind(true); }); listener$j.follow('subsview', function (e) { PlayerVideo.subsview(e.status); }); listener$j.follow('to_start', function () { PlayerVideo.to(0); }); listener$j.follow('fullscreen', function () { Utils$1.toggleFullscreen(); }); listener$j.follow('pip', function () { PlayerVideo.pip(); }); } /** * Cкрыть панель перемотки */ function hideRewind() { Html.render().addClass('panel--norewind'); } /** * Добавить контроллеры */ function addController() { Controller.add('player_tv', { invisible: true, toggle: function toggle() { Controller.clear(); condition.visible = false; state.start(); }, up: function up() { PlayerIPTV.prevChannel(); state.start(); }, down: function down() { PlayerIPTV.nextChannel(); state.start(); }, left: function left() { condition.visible = true; PlayerIPTV.openMenu(); state.start(); }, right: function right() { condition.visible = true; IptvPanel.settings(); state.start(); }, enter: function enter() { PlayerIPTV.play(); state.start(); }, back: function back() { PlayerIPTV.reset(); Controller.toggle('player'); hide$1(); } }); Controller.add('player_rewind', { toggle: function toggle() { Controller.collectionSet(render$e()); Controller.collectionFocus(false, render$e()); }, up: function up() { toggleUp(); }, down: function down() { toggleButtons(); }, right: function right() { listener$j.send('rnext', {}); }, left: function left() { listener$j.send('rprev', {}); }, gone: function gone() { Html.render().find('.selector').removeClass('focus'); }, back: toPlayer }); Controller.add('player_panel', { toggle: function toggle() { if (PlayerIPTV.playning()) Controller.toggle('player_tv');else { Controller.collectionSet(render$e()); Controller.collectionFocus($(Player.playdata().iptv ? '.player-panel__next' : '.player-panel__playpause', Html.render())[0], render$e()); } }, up: function up() { Player.playdata().iptv || Html.render().hasClass('panel--norewind') ? toggleUp() : toggleRewind(); }, right: function right() { Navigator.move('right'); }, left: function left() { Navigator.move('left'); }, down: function down() { PlayerFooter.available() ? Controller.toggle('player_footer') : listener$j.send('playlist', {}); }, gone: function gone() { Html.render().find('.selector').removeClass('focus'); }, back: toPlayer }); } /** * Рассчитать проценты * @param {object} e * @returns {number} */ function percent(e) { var offset = Html.elem('timeline').offset(); var width = Html.elem('timeline').width(); return (e.clientX - offset.left) / width; } /** * Обновляем состояние панели * @param {string} need - что нужно обновить * @param {string|number} value - значение */ function update$d(need, value) { if (need == 'position') { timeline_last.position = value; if (panel_visible) Html.elem('position').css({ width: value }); } if (need == 'peding') { timeline_last.peding = value; if (panel_visible) Html.elem('peding').css({ width: value }); } if (need == 'timeend') { Html.elem('timeend').text(value); } if (need == 'timenow') { Html.elem('timenow').text(value); } if (need == 'play') { Html.render().toggleClass('panel--paused', false); } if (need == 'pause') { Html.render().toggleClass('panel--paused', true); } } /** * Показать или скрыть панель * @param {boolean} status */ function _visible(status) { panel_visible = status; listener$j.send('visible', { status: status }); Html.render().toggleClass('panel--visible', status); Html.elem('position').css({ width: timeline_last.position }); Html.elem('peding').css({ width: timeline_last.peding }); } /** * Получили событие canplay, теперь можно скрывать панель и отслеживать статус */ function canplay() { condition.canplay = true; state.start(); } /** * Запустили режим перемотки, показываем панель и через 3 секунды скрываем */ function rewind$1() { condition.rewind = true; state.start(); } /** * Переключить на контроллер перемотки */ function toggleRewind() { Controller.toggle(Player.playdata().iptv || Html.render().hasClass('panel--norewind') ? 'player_panel' : 'player_rewind'); } /** * Уйти вверх с панели: на кнопку пропуска, если она показана, иначе на плеер */ function toggleUp() { if (Skip.isActive()) Skip.toggle();else Controller.toggle('player'); } /** * Уйти на плеер и скрыть панель */ function toPlayer() { Controller.toggle('player'); hide$1(); } /** * Показать панель, не переключая контроллер */ function reveal() { condition.visible = true; state.start(); } /** * Переключить на контроллер кнопки */ function toggleButtons() { if (!Platform.screen('mobile')) Controller.toggle('player_panel'); } /** * Переключить на контроллер панели */ function toggle$9() { condition.visible = true; state.start(); if (PlayerIPTV.playning()) Controller.toggle('player_tv');else if (!Platform.screen('mobile')) toggleButtons(); } /** * Запустили плеер и нужно запустить статус панели */ function show$9() { state.start(); Html.elem('fullscreen').toggleClass('hide', Platform.tv() || Platform.is('android') || !Utils$1.canFullScreen()); addController(); } /** * Если двигали мышку */ function mousemove() { condition.mousemove = true; state.start(); } /** * Скрыть панель */ function hide$1() { condition.visible = false; condition.mousemove = false; _visible(false); } /** * Состояние видимости панели * @returns {boolean} */ function visibleStatus() { return panel_visible; } /** * Уничтожить */ function destroy$g() { condition = {}; timeline_last.position = 0; timeline_last.peding = 0; panel_visible = false; Option.destroy(); Apex.destroy(); Segments.destroy(); Next$2.destroy(); Html.elem('peding').css({ width: 0 }); Html.elem('position').css({ width: 0 }); Html.elem('time').text('00:00'); Html.elem('timenow').text('00:00'); Html.elem('timeend').text('00:00'); Html.elem('episode').toggleClass('hide', true); Html.elem('playlist_buttons').toggleClass('hide', true); Html.render().toggleClass('panel--paused', false); Html.render().toggleClass('panel--norewind', false); Html.render().toggleClass('panel--footer-open', false); } /** * Получить html * @returns {object} */ function render$e() { return Html.render(); } var PlayerPanel = { init: init$16, listener: listener$j, render: render$e, toggle: toggle$9, show: show$9, destroy: destroy$g, hide: hide$1, toPlayer: toPlayer, reveal: reveal, canplay: canplay, update: update$d, rewind: rewind$1, mousemove: mousemove, quality: Option.setQualitys, updateTranslate: Option.updateTranslate, visible: _visible, visibleStatus: visibleStatus, hideRewind: hideRewind, setTranslate: Option.setTranslates, setTracks: Option.setTracks, setSubs: Option.setSubtitles, setLevels: Option.setLevels, setFlows: Option.setFlows }; var html$i; var listener$d = Subscribe$2(); var network$a = new Request(); var elems; var error$2, stat_timer; function init$15() { html$i = Template.get('player_info'); html$i.find('.player-info__body').prepend(HeadBackward(Lang.translate('settings_main_player'))); elems = { name: $('.player-info__name,.head-backward__title', html$i), title: $('.player-info__title', html$i), size: $('.value--size span', html$i), stat: $('.value--stat span', html$i), speed: $('.value--speed span', html$i), vname: $('.value--name', html$i), error: $('.player-info__error', html$i), timeend: $('.player-info__time-end span', html$i), pieces: $('.value--pieces', html$i) }; Utils$1.time(html$i); PlayerVideo.listener.follow('videosize', function (e) { set$1('size', e); }); PlayerVideo.listener.follow('destroy', function (e) { set$1('bitrate', ''); }); PlayerVideo.listener.follow('timeupdate', updateTimeEnd); PlayerVideo.listener.follow('error', function (e) { if (e.fatal) elems.size.text(Lang.translate('title_error'));else set$1('error', e.error); }); PlayerPanel.listener.follow('visible', function (e) { toggle$8(e.status); updateTimeEnd(); }); Player.listener.follow('start', function (data) { set$1('name', data.title); if (Torserver.ip() && data.url.indexOf(Torserver.ip()) > -1) set$1('stat', data); }); Player.listener.follow('destroy', destroy$f); } function updateTimeEnd() { var video = PlayerVideo.video(); if (PlayerPanel.visibleStatus() && video && video.duration) { elems.timeend.text(Lang.translate('player_time_end') + ' ' + Utils$1.parseTime(new Date(Date.now() + (video.duration - video.currentTime) * 1000)).time); } else { elems.timeend.text(''); } } /** * Установить значение * @param {string} need * @param {string|{width,height}} value */ function set$1(need, value) { if (need == 'name') { var name = value; var work = Player.playdata(); var head = ''; if (!work.iptv) { if (work.card) head = work.card.title || work.card.name;else if (Lampa.Activity.active().movie) { head = Lampa.Activity.active().movie.title || Lampa.Activity.active().movie.name; } if (!head) head = name; elems.title.text(head).toggleClass('hide', Boolean(work.iptv)); elems.name.toggleClass('hide', Boolean(name == head)).toggleClass('hide', true); elems.vname.toggleClass('hide', Boolean(name == head)).find('span').text(name); } else elems.title.text(name); } else if (need == 'size' && value.width && value.height) elems.size.text(value.width + 'x' + value.height);else if (need == 'error') { clearTimeout(error$2); elems.error.removeClass('hide').text(value); error$2 = setTimeout(function () { elems.error.addClass('hide'); }, 10000); } else if (need == 'stat') stat$3(value);else if (need == 'bitrate' && !stat_timer) elems.stat.html(value); } function pieces(cache) { elems.pieces.empty(); if (cache.Readers.length) { var reader = cache.Readers[0].Reader; var end = cache.Readers[0].End; var start = reader; var total = end - reader; var dots = 5; var loaded = 0; while (cache.Pieces[start] && cache.Pieces[start].Completed && start < end) { start++; loaded++; } var percent = loaded / total * 100; for (var i = 0; i < dots; i++) { var color = ''; var filled = Math.round(dots * (loaded / total)) >= i; if (i == 0) { if (percent > 80) color = 'green';else if (percent >= 40) color = 'yellow';else color = 'red'; } else if (filled) color = 'active'; elems.pieces.append(''); } } } /** * Показываем статистику по торренту * @param {object} player_data */ function stat$3(player_data) { var wait = 0; elems.stat.text('- / - • - ' + Lang.translate('connected_seeds')); elems.speed.text('--'); var update = function update() { // если панель скрыта, то зачем каждую секунду чекать? хватит и 5 сек // проверено, если ставить на паузу, разадача удаляется, но если чекать постоянно, то все норм if (!html$i.hasClass('info--visible')) { wait++; if (wait <= 5) return;else wait = 0; } network$a.timeout(2000); var url = ''; if (Torserver.gstWork()) url = Torserver.url() + '/gst/' + player_data.torrent_hash + '/heartbeat';else url = player_data.url.replace('&preload', '&stat').replace('&play', '&stat'); network$a.silent(url, function (data) { var torrent = data.Torrent || data; elems.stat.text((torrent.active_peers || 0) + ' / ' + (torrent.total_peers || 0) + ' • ' + (torrent.connected_seeders || 0) + ' ' + Lang.translate('connected_seeds')); elems.speed.text(Utils$1.bytesToSize(torrent.download_speed ? torrent.download_speed * 8 : 0, true)); if (Torserver.gstWork()) return pieces(data); var hash = url.match(/link=(.*?)\&/); if (hash) { Torserver.cache(hash[1], function (cache) { pieces(cache); listener$d.send('stat', { data: data, cache: cache }); }, function () { listener$d.send('stat', { data: data }); }); } else { listener$d.send('stat', { data: data }); } }); }; stat_timer = setInterval(update, 2000); update(); } /** * Показать скрыть инфо * @param {boolean} status */ function toggle$8(status) { html$i.toggleClass('info--visible', status); } function loading$2() { elems.size.text(Lang.translate('loading') + '...'); } /** * Уничтожить */ function destroy$f() { elems.size.text(Lang.translate('loading') + '...'); elems.stat.text(''); elems.speed.text(''); elems.error.addClass('hide'); elems.pieces.empty(); elems.vname.toggleClass('hide', true); elems.name.toggleClass('hide', true); elems.title.toggleClass('hide', false); clearTimeout(error$2); clearInterval(stat_timer); network$a.clear(); } function render$d() { return html$i; } var PlayerInfo = { init: init$15, listener: listener$d, render: render$d, set: set$1, toggle: toggle$8, loading: loading$2, destroy: destroy$f }; var cues_timer; /** * Отрисовка субтитров в плеере * @param {String} text - Текст субтитров * @param {String} style - Стиль субтитров (например, 'bold', 'italic', 'underline') */ function draw$4(text, style) { var cues = render$c(); var inner = $('> div', cues); inner.removeClass('bold italic underline'); if (style) inner.addClass(style); inner.html(text ? text : ' ').css({ display: text ? 'inline-block' : 'none' }); clearTimeout(cues_timer); cues_timer = setTimeout(function () { inner.html(' ').css({ display: 'none' }); }, 10000); } function render$c() { return PlayerVideo.render().find('.player-video__subtitles'); } var Cue = { draw: draw$4, render: render$c }; var _hls = null; var _hls_parser = null; var _hls_subs_cues = {}; var _hls_subs_active_track = -1; function levelName(level) { var level_width = level.width || 0; var level_height = level.height || 0; var levels = [240, 360, 480, 720, 1080, 1440, 2160]; var name = levels.find(function (size) { var quality_width = Math.round(size * 1.777); var quality_height = size; var w = level_width > quality_width - 50 && level_width < quality_width + 50; var h = level_height > quality_height - 50 && level_height < quality_height + 50; return w || h; }); return name ? name + 'p' : level.qu ? level.qu : level.width ? level.height + 'p' : 'AUTO'; } function levelDefault(where) { var start_level = where.levels.find(function (level) { var level_width = level.width || 0; var level_height = level.height || 0; var quality_width = Math.round(Storage.field('video_quality_default') * 1.777); var quality_height = Storage.field('video_quality_default'); var w = level_width > quality_width - 50 && level_width < quality_width + 50; var h = level_height > quality_height - 50 && level_height < quality_height + 50; return w || h; }); return start_level ? where.levels.indexOf(start_level) : where.currentLevel; } function bitrate(seconds) { if (_hls && _hls.streamController && _hls.streamController.fragPlaying && _hls.streamController.fragPlaying.baseurl && _hls.streamController.fragPlaying.stats) { var ch = Lang.translate('title_channel') + ' ' + parseFloat(_hls.streamController.fragLastKbps / 1000).toFixed(2) + ' ' + Lang.translate('speed_mb'); var bt = '  •  ' + Lang.translate('torrent_item_bitrate') + ' ~' + parseFloat(_hls.streamController.fragPlaying.stats.total / 1000000 / 10 * 8).toFixed(2) + ' ' + Lang.translate('speed_mb'); var bf = '  •  ' + Lang.translate('title_buffer') + ' ' + Utils$1.secondsToTimeHuman(seconds); PlayerInfo.set('bitrate', ch + bt + bf); } } function update$c(seconds) { if (!_hls || _hls_subs_active_track < 0) return; var cues = _hls_subs_cues[_hls_subs_active_track]; if (!cues) return; var active = null; for (var i = 0; i < cues.length; i++) { var c = cues[i]; if (c.startTime <= seconds && seconds < c.endTime) { active = c; break; } } Cue.draw(active ? active.text || '' : ''); } function shouldUseProgram(videoEl, playdata) { var use_program = Storage.field('player_hls_method') == 'hlsjs' || Platform.chromeVersion() > 120; var hls_type = playdata.hls_type; var hls_native = videoEl.canPlayType('application/vnd.apple.mpegurl'); if (Platform.is('tizen') && Storage.field('player') == 'tizen') use_program = false;else if (Platform.is('orsay') && Storage.field('player') == 'orsay') use_program = false;else if (!use_program && !hls_native) use_program = true; if (hls_type == 'hlsjs') use_program = true;else if (hls_type == 'native' && hls_native) use_program = false; if (!Hls.isSupported()) use_program = false; return { use_program: use_program, hls_native: hls_native }; } function createProgram(src, videoEl, playdata, callbacks) { var timeout = playdata.hls_manifest_timeout || 10000; console.log('Player', 'hls start program'); _hls = new Hls({ manifestLoadTimeout: timeout, manifestLoadMaxRetryTimeout: playdata.hls_retry_timeout || 30000, renderTextTracksNatively: false, xhrSetup: function xhrSetup(xhr) { xhr.timeout = timeout; xhr.ontimeout = function () { console.log('Player', 'hls manifestLoadTimeout'); }; } }); _hls.loadSource(src); _hls.attachMedia(videoEl); _hls.on(Hls.Events.ERROR, function (_, data) { console.log('Player', 'hls error', data.reason, data.details, data.fatal); if (data.details === Hls.ErrorDetails.MANIFEST_PARSING_ERROR) { if (data.reason === "no EXTM3U delimiter") { callbacks.load(src); } else { callbacks.error('details [' + data.details + '] fatal [' + data.fatal + ']', data.fatal); } } else { callbacks.error('details [' + data.details + '] fatal [' + data.fatal + ']', data.fatal); } }); _hls.on(Hls.Events.MANIFEST_LOADED, function () { callbacks.play(); }); _hls.on(Hls.Events.MANIFEST_PARSED, function () { _hls.currentLevel = levelDefault(_hls); }); _hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, function (_event, data) { if (!data.subtitleTracks || !data.subtitleTracks.length) return; _hls_subs_cues = {}; _hls_subs_active_track = -1; var subs = data.subtitleTracks.map(function (track, i) { var sub = { index: i, label: track.name || track.lang || 'Subtitle ' + (i + 1), selected: false }; Object.defineProperty(sub, 'mode', { set: function set(v) { if (v == 'showing') { _hls_subs_active_track = i; _hls.subtitleTrack = i; Cue.render().removeClass('hide'); } else { if (_hls_subs_active_track == i) { _hls_subs_active_track = -1; _hls.subtitleTrack = -1; Cue.draw(''); } } }, get: function get() { return _hls_subs_active_track == i ? 'showing' : 'disabled'; } }); return sub; }); callbacks.subtitles(subs); }); _hls.on(Hls.Events.CUES_PARSED, function (_event, data) { var idx = _hls.subtitleTrack; if (idx < 0) return; if (!_hls_subs_cues[idx]) _hls_subs_cues[idx] = []; data.cues.forEach(function (cue) { _hls_subs_cues[idx].push(cue); }); }); } function createParser(src, playdata, callbacks) { var timeout = playdata.hls_manifest_timeout || 10000; var send_load_ready = false; console.log('Player', 'hls start parse'); _hls_parser = new Hls({ manifestLoadTimeout: timeout, manifestLoadMaxRetryTimeout: playdata.hls_retry_timeout || 30000, xhrSetup: function xhrSetup(xhr) { xhr.timeout = timeout; xhr.ontimeout = function () { console.log('Player', 'hls manifestLoadTimeout'); }; } }); _hls_parser.loadSource(src); _hls_parser.on(Hls.Events.ERROR, function (_, data) { console.log('Player', 'hls parse error', data.reason, data.details, data.fatal); if (!send_load_ready) callbacks.load(src); }); _hls_parser.on(Hls.Events.MANIFEST_LOADED, function () { if (_hls_parser.audioTracks.length) callbacks.translate('tracks', _hls_parser.audioTracks.map(function (a) { return { name: a.name }; })); if (_hls_parser.subtitleTracks.length) callbacks.translate('subs', _hls_parser.subtitleTracks.map(function (a) { return { label: a.name }; })); console.log('Player', 'parse hls audio', _hls_parser.audioTracks.length, _hls_parser.audioTracks.map(function (a) { return a.name; })); console.log('Player', 'parse hls subs', _hls_parser.subtitleTracks.length, _hls_parser.subtitleTracks.map(function (a) { return a.name; })); if (!_hls_parser.audioTracks.length) { var start_level = levelDefault(_hls_parser); var select_level = start_level >= 0 ? _hls_parser.levels[start_level] : _hls_parser.levels[_hls_parser.levels.length - 1]; var parsed_levels = _hls_parser.levels.map(function (level) { return { title: levelName(level), change_quality: true, url: level.url[0], selected: level === select_level }; }); console.log('Player', 'set hls levels', parsed_levels); callbacks.levels(parsed_levels, levelName(select_level)); console.log('Player', 'hls select level url:', select_level.url[0]); callbacks.load(select_level.url[0]); } else callbacks.load(src); send_load_ready = true; }); } function setupAudioTracks$1() { var tracks = audioTracks$1(); if (!tracks) return null; tracks.forEach(function (track) { if (_hls.audioTrack == track.id) track.selected = true; Object.defineProperty(track, "enabled", { set: function set(v) { if (v) _hls.audioTrack = track.id; }, get: function get() {} }); }); return tracks; } function audioTracks$1() { return _hls && _hls.audioTracks && _hls.audioTracks.length ? _hls.audioTracks : null; } function buildLevels$1(savedLevel) { if (!_hls || !_hls.levels) return null; var current_level = 'AUTO'; _hls.levels.forEach(function (level, i) { level.title = levelName(level); if (_hls.currentLevel == i) { current_level = level.title; level.selected = true; } Object.defineProperty(level, "enabled", { set: function set(v) { if (v) { _hls.currentLevel = i; _hls.levels.map(function (e) { return e.selected = false; }); level.selected = true; } }, get: function get() {} }); }); if (typeof savedLevel !== 'undefined' && _hls.levels[savedLevel]) { _hls.levels.map(function (e) { return e.selected = false; }); _hls.levels[savedLevel].enabled = true; _hls.levels[savedLevel].selected = true; current_level = _hls.levels[savedLevel].title; } else { if (_hls.currentLevel >= 0) current_level = _hls.levels[_hls.currentLevel].title; } return { levels: _hls.levels, current: current_level }; } function currentLevel$1() { return _hls ? _hls.currentLevel : undefined; } function isActive$1() { return !!_hls; } function destroyParser() { if (_hls_parser) { try { _hls_parser.destroy(); } catch (e) {} _hls_parser = null; } } function destroy$e() { destroyParser(); _hls_subs_cues = {}; _hls_subs_active_track = -1; if (_hls) { try { _hls.destroy(); } catch (e) {} _hls = null; return true; } return false; } var HlsStream = { bitrate: bitrate, update: update$c, shouldUseProgram: shouldUseProgram, createProgram: createProgram, createParser: createParser, setupAudioTracks: setupAudioTracks$1, audioTracks: audioTracks$1, buildLevels: buildLevels$1, currentLevel: currentLevel$1, isActive: isActive$1, destroy: destroy$e, destroyParser: destroyParser }; var _dash = null; function create$a(src, videoEl, callbacks) { try { if (Platform.is('orsay') && Storage.field('player') == 'orsay') { callbacks.load(src); } else { _dash = dashjs.MediaPlayer().create(); _dash.getSettings().streaming.abr.autoSwitchBitrate = false; _dash.initialize(videoEl, src, true); } } catch (e) { console.log('Player', 'Dash error:', e.stack); callbacks.load(src); } } function setupAudioTracks() { if (!_dash) return null; var tracks = _dash.getTracksFor('audio'); if (!tracks || !tracks.length) return null; tracks.forEach(function (track, i) { if (i == 0) track.selected = true; track.language = (track.lang + '').replace(/\d+/g, ''); Object.defineProperty(track, "enabled", { set: function set(v) { if (v) _dash.setCurrentTrack(track); }, get: function get() {} }); }); return tracks; } function audioTracks() { if (!_dash) return null; var tracks = _dash.getTracksFor('audio'); return tracks && tracks.length ? tracks : null; } function buildLevels(savedLevel) { if (!_dash) return null; var bitrates = _dash.getBitrateInfoListFor("video"); var current_level = 'AUTO'; bitrates.forEach(function (level, i) { level.title = level.width ? level.width + 'x' + level.height : 'AUTO'; if (i == 0) current_level = level.title; Object.defineProperty(level, "enabled", { set: function set(v) { if (v) { _dash.getSettings().streaming.abr.autoSwitchBitrate = false; _dash.setQualityFor("video", level.qualityIndex); } }, get: function get() {} }); }); if (typeof savedLevel !== 'undefined' && bitrates[savedLevel]) { bitrates.map(function (e) { return e.selected = false; }); _dash.getSettings().streaming.abr.autoSwitchBitrate = false; bitrates[savedLevel].enabled = true; bitrates[savedLevel].selected = true; current_level = bitrates[savedLevel].title; } return { levels: bitrates, current: current_level }; } function currentLevel() { return _dash ? _dash.getQualityFor('video') : undefined; } function isActive() { return !!_dash; } function destroy$d() { if (_dash) { try { _dash.destroy(); } catch (e) {} _dash = null; return true; } return false; } var DashStream = { create: create$a, setupAudioTracks: setupAudioTracks, audioTracks: audioTracks, buildLevels: buildLevels, currentLevel: currentLevel, isActive: isActive, destroy: destroy$d }; var context; function smooth(a, b, s, c) { return a + (b - a) * (s * 0.02); } function toDb(_float) { var db = 20 * (Math.log(_float) / Math.log(10)); db = Math.max(-48, Math.min(db, 0)); return db; } function Source(video) { var source = context.createMediaElementSource(video); var analyser = context.createAnalyser(); var volume = context.createGain(); var destroy = false; var display = true; var draw_html = $('
    '); var draw_canvas = draw_html.find('canvas')[0]; var draw_context = draw_canvas.getContext("2d"); draw_canvas.width = 5; draw_canvas.height = Math.round(window.innerHeight * 0.26); try { analyser.fftSize = 2048 * 4; console.log('Player', 'normalization fftSize 2048 * 4'); } catch (e) { try { analyser.fftSize = 2048 * 2; console.log('Player', 'normalization fftSize 2048 * 2'); } catch (e) { analyser.fftSize = 2048; console.log('Player', 'normalization fftSize 2048'); } } var frequencyData = new Uint8Array(analyser.frequencyBinCount); var midFreqRange = { start: 1300, end: 4000 }; // Средние частоты var highFreqRange = { start: 4000, end: 8000 }; // Высокие частоты function calculateRMS(range) { var startIndex = Math.floor(range.start * analyser.frequencyBinCount / (context.sampleRate / 2)); var endIndex = Math.floor(range.end * analyser.frequencyBinCount / (context.sampleRate / 2)); startIndex = Math.max(0, Math.min(startIndex, frequencyData.length - 1)); endIndex = Math.max(0, Math.min(endIndex, frequencyData.length - 1)); var total = 0; var count = 0; for (var i = startIndex; i <= endIndex; i++) { var value = frequencyData[i]; total += value * value; count++; } return count > 0 ? Math.sqrt(total / count) : 0; } analyser.gain_smooth = 1; //подключаем анализ source.connect(analyser); //подключаем регулятор звука analyser.connect(volume); //подключаем к выходу volume.connect(context.destination); $('body').append(draw_html); function update() { if (!destroy) requestAnimationFrame(update); analyser.getByteFrequencyData(frequencyData); var rms_mid = calculateRMS(midFreqRange); var rms_high = calculateRMS(highFreqRange); var db_mid = toDb(rms_mid / 255); var db_high = toDb(rms_high / 255); var sm_st = Storage.get('player_normalization_smooth', 'medium'); var pw_st = Storage.get('player_normalization_power', 'hight'); var tp_st = Storage.get('player_normalization_type', 'all'); var pw_am = pw_st == 'hight' ? 1 : pw_st == 'medium' ? 0.7 : 0.35; var gain = Math.max(0.0, Math.min(2, (db_mid + db_high) / 2 / -24)); analyser.gain_smooth = sm_st == 'none' ? gain : smooth(analyser.gain_smooth, gain, sm_st == 'hight' ? 45 : sm_st == 'medium' ? 20 : 5); var vol = pw_st == 'none' ? 1 : 1 + (analyser.gain_smooth - 1) * pw_am; volume.gain.value = tp_st == 'all' ? vol : tp_st == 'up' ? Math.max(1, vol) : Math.min(1, vol); if (display) { draw_context.clearRect(0, 0, draw_canvas.width, draw_canvas.height); var down = Math.min(1, Math.max(0, 1 - volume.gain.value)); var up = Math.min(1, Math.max(0, volume.gain.value - 1)); var half = draw_canvas.height / 2; draw_context.fillStyle = 'rgba(251,91,91,1)'; draw_context.fillRect(0, half, draw_canvas.width, half * down); draw_context.fillStyle = 'rgba(91,213,251,1)'; draw_context.fillRect(0, half - half * up, draw_canvas.width, half * up); } } update(); this.visible = function (status) { display = status; draw_html.toggleClass('normalization--visible', status); }; this.destroy = function () { volume.disconnect(); analyser.disconnect(); source.disconnect(); destroy = true; draw_html.remove(); }; } function Normalization() { if (!context) { var classContext = window.AudioContext || window.webkitAudioContext; context = new classContext(); } var source; this.attach = function (video) { if (!source) source = new Source(video); }; this.visible = function (status) { if (source) source.visible(status); }; this.destroy = function () { if (source) source.destroy(); source = null; }; } function AVPlay(call_video) { var avplay, plugin, stream_url, loaded, current_time; var object = $(''); var video = object[0]; var listener = Subscribe$2(); var change_scale_later; var change_speed_later; var canGetVideoResolution = false; /** * Установить урл */ Object.defineProperty(video, "src", { set: function set(url) { if (url) { if (url.toLowerCase().indexOf(".m3u8") != -1 && url.toUpperCase().indexOf("|COMPONENT=HLS") == -1) { url += "|COMPONENT=HLS"; } else if (url.toLowerCase().indexOf(".mpd") != -1 && url.toUpperCase().indexOf("|COMPONENT=HAS") == -1) { url += "|COMPONENT=HAS"; } stream_url = url; console.log(stream_url); avplay.init(); plugin = avplay.setPlayerPluginObject(); avplay.onEvent = eventHandler; } }, get: function get() {} }); /** * Позиция */ Object.defineProperty(video, "currentTime", { set: function set(t) { try { seekTo(t); } catch (e) { listener.send('error', { error: 'code [' + e.code + '] ' + e.message }); } }, get: function get() { return current_time ? current_time / 1000 : 0; } }); /** * */ function seekTo(time) { var time_s; var time_ms = Math.ceil(time) * 1000; var duration_ms = avplay.getDuration(); if (time_ms >= 0 && current_time - time_ms >= 0) { time_s = Math.ceil((current_time - time_ms) / 1000); plugin.Execute("JumpBackward", time_s); } else if (time_ms >= 0 && duration_ms - current_time + time_ms >= 0) { time_s = Math.ceil((time_ms - current_time) / 1000); plugin.Execute("JumpForward", time_s); } } /** * Длительность */ Object.defineProperty(video, "duration", { set: function set() {}, get: function get() { var d = 0; try { d = avplay.getDuration(); } catch (e) {} return d ? d / 1000 : 0; } }); /** * Пауза */ Object.defineProperty(video, "paused", { set: function set() {}, get: function get() { try { return avplay.status == '5'; } catch (e) { return false; } } }); /** * Аудиодорожки */ Object.defineProperty(video, "audioTracks", { set: function set() {}, get: function get() { try { var amount = avplay.getTotalNumOfStreamID(1); var tracks = Array.apply(null, { length: amount }).map(Number.call, Number).map(function (i) { var item = { extra: JSON.parse(avplay.getStreamExtraData(1, i)), index: parseInt(i), language: languageNumToStr(avplay.getStreamLanguageInfo(1, i)) }; Object.defineProperty(item, "enabled", { set: function set(v) { if (v) { try { avplay.setStreamID(1, item.index); } catch (e) { console.log('Player', 'no change audio:', e.message); } } }, get: function get() {} }); return item; }).sort(function (a, b) { return a.index - b.index; }); return tracks; } catch (e) { return []; } } }); /** * Субтитры */ Object.defineProperty(video, "textTracks", { set: function set() {}, get: function get() { try { var amount = avplay.getTotalNumOfStreamID(4); var tracks = Array.apply(null, { length: amount }).map(Number.call, Number).map(function (i) { var item = { extra: JSON.parse(avplay.getStreamExtraData(4, i)), index: parseInt(i), language: languageNumToStr(avplay.getStreamLanguageInfo(4, i)) }; Object.defineProperty(item, "mode", { set: function set(v) { if (v == 'showing') { try { avplay.setStreamID(4, item.index); } catch (e) { console.log('Player', 'no change text:', e.message); } } }, get: function get() {} }); return item; }).sort(function (a, b) { return a.index - b.index; }); return tracks; } catch (e) { return []; } } }); /** * Получаем текст языка субтитров или аудио */ function languageNumToStr(num) { var str; //Словарь кодов озвучек var lang = { 6384738: "Albanian", 7565673: "Albanian", 6647399: "English", 6388325: "Azerbaijan", 6386285: "Armenian", 6448492: "Belarusian", 6452588: "Bulgarian", 6514793: "Chinese", 6776178: "German", 6911073: "Italian", 7565409: "Spanish", 7037306: "Kazakh", 7040882: "Korean", 7368562: "Portuguese", 7501171: "Russian", 7564399: "Slovak", 7564406: "Slovenian", 7565936: "Serbian", 7632242: "Turkish", 7699042: "Uzbek", 7695218: "Ukrainian", 8026747: "Ukrainian", 6713957: "French", 7567205: "Swedish", 6975598: "Japanese" }; if (lang[num] != undefined) { str = lang[num]; } else { var nHex = num.toString(16); var sHex1 = "0x" + nHex.substring(0, 2); var sHex2 = "0x" + nHex.substring(2, 2); var sHex3 = "0x" + nHex.substring(4, 2); var str1 = String.fromCharCode(sHex1); var str2 = String.fromCharCode(sHex2); var str3 = String.fromCharCode(sHex3); str = str1 + str2 + str3; if (str === "\0\0\0") { str = "Неизвестный"; } } return str; } /** * Ширина видео */ Object.defineProperty(video, "videoWidth", { set: function set() {}, get: function get() { if (canGetVideoResolution) { return avplay.getVideoResolution().split('|')[0]; } else { return 0; } } }); /** * Высота видео */ Object.defineProperty(video, "videoHeight", { set: function set() {}, get: function get() { if (canGetVideoResolution) { return avplay.getVideoResolution().split('|')[1]; } else { return 0; } } }); /** * Меняем размер видео * @param {string} scale - default|fill */ function changeScale(scale) { try { var xV = curWidget.width, yV = curWidget.height, aX = 0, aY = 0, aW = curWidget.width, aH = curWidget.height, cX = 0, cY = 0, pH = 100, pW = 100, cW = video.videoWidth, cH = video.videoHeight; if (cH == 0 || cW == 0) { throw false; } switch (scale) { //original case 'default': if (cW / cH < 1.79) { aW = yV * cW / cH; aX = (xV - aW) / 2; } else { aH = xV * cH / cW; aY = (yV - aH) / 2; } ; break; //full case 'fill': break; default: //zoom var type = scale[0]; var zoom = parseInt(scale.replace(/\D+/g, "")); if (type == 's') { pH = zoom; pW = zoom; } else if (type == 'v') { pH = zoom; pW = 100; } if (zoom >= 80 && zoom <= 140) { if (pW <= 100) { aW = xV / 100 * pW; aX = (xV - aW) / 2; } else { cX = cW * (pW / 200 - 0.5); cW = cW * (2 - pW / 100); } if (pH <= 100) { aH = yV / 100 * pH; aY = (yV - aH) / 2; } else { cY = cH * (pH / 200 - 0.5); cH = cH * (2 - pH / 100); } } else { changeScale('default'); return; } break; } ; avplay.setDisplayArea({ left: aX, top: aY, width: aW, height: aH }); if (scale != 'fill') { avplay.setCropArea(function () { console.log('Player', 'change scale ' + scale); }, function (e) { listener.send('error', { error: 'code [' + e.code + '] ' + e.message }); }, { left: cX, top: cY, width: cW, height: cH }); } else { avplay.setCropArea(function () { console.log('Player', 'change scale ' + scale); }, function (e) { listener.send('error', { error: 'code [' + e.code + '] ' + e.message }); }, { left: 0, top: 0, width: 0, height: 0 }); } } catch (e) { change_scale_later = scale; } } function changeSpeed(speed) { try { avplay.setSpeed(speed); } catch (e) { change_speed_later = speed; } } /** * Всегда говорим да, мы можем играть */ video.canPlayType = function () { return true; }; /** * Вешаем кастомные события */ video.addEventListener = listener.follow.bind(listener); /** * Вешаем события от плеера orsay */ var eventHandler = function eventHandler(type, data) { switch (type) { // 1 CONNECTION_FAILED; case 1: listener.send('error', { error: '[orsay native player: CONNECTION_FAILED]' }); break; // 2 AUTHENTICATION_FAILED case 2: listener.send('error', { error: '[orsay native player: AUTHENTICATION_FAILED]' }); break; // 3 STREAM_NOT_FOUND case 3: listener.send('error', { error: '[orsay native player: STREAM_NOT_FOUND]' }); break; // 4 NETWORK_DISCONNECTED case 4: listener.send('error', { error: '[orsay native player: NETWORK_DISCONNECTED]' }); break; // 5 NETWORK_SLOW case 5: listener.send('error', { error: '[orsay native player: NETWORK_SLOW]' }); break; // 6 RENDER_ERROR (a) case 6: switch (data) { case "0": listener.send('error', { error: '[orsay native player: UNKNOWN_ERROR]' }); break; case "1": listener.send('error', { error: '[orsay native player: UNSUPPORTED_CONTAINER]' }); break; case "2": listener.send('error', { error: '[orsay native player: UNSUPPORTED_VIDEO_CODEC]' }); break; case "3": listener.send('error', { error: '[orsay native player: UNSUPPORTED_AUDIO_CODEC]' }); break; case "4": listener.send('error', { error: '[orsay native player: UNSUPPORTED_VIDEO_RESOLUTION]' }); break; case "5": listener.send('error', { error: '[orsay native player: UNSUPPORTED_VIDEO_FRAMERATE]' }); break; case "6": listener.send('error', { error: '[orsay native player: CURRUPTED_STREAM]' }); break; case "100": listener.send('error', { error: '[orsay native player: CUSTOM_ERROR]' }); break; } // 7 RENDERING_START case 7: console.log('Player', 'RENDERING_START'); break; // 8 RENDERING_COMPLETE case 8: console.log('Player', 'RENDERING_COMPLETE'); avplay.stop(); listener.send('ended'); break; // 9 STREAM_INFO_READY case 9: canGetVideoResolution = true; $('body').toggleClass('orsay-player--show', true); console.log('Player', 'STREAM_INFO_READY'); avplay.startSubtitle({ path: "/dtv/temp/", streamID: 999, sync: 999, callback: function callback() {} }); listener.send('loadeddata'); break; // 10 DECODING_COMPLETE case 10: console.log('Player', 'DECODING_COMPLETE'); break; case 11: console.log('Player', 'buffering start'); listener.send('waiting'); break; // 12 BUFFERING_COMPLETE case 12: console.log('Player', 'BUFFERING_COMPLETE'); listener.send('playing'); break; // 13 BUFFERING_PROGRESS case 13: listener.send('progress', { percent: data }); break; // 14 CURRENT_PLAYBACK_TIME case 14: current_time = data; listener.send('timeupdate'); if (change_scale_later) { changeScale(change_scale_later); change_scale_later = false; } if (change_speed_later) { changeSpeed(change_speed_later); change_speed_later = false; } break; // 15 AD_START case 15: console.log('Player', 'AD_START'); break; // 16 AD_END case 16: console.log('Player', 'AD_END'); break; // 17 RESOLUTION_CHANGED case 17: console.log('Player', 'RESOLUTION_CHANGED ' + data); break; // 18 BITRATE_CHANGED case 18: console.log('Player', 'BITRATE_CHANGED ' + data); break; // 19 SUBTITLE case 19: listener.send('subtitle', { text: data }); break; } }; /** * Загрузить */ video.load = function () { if (stream_url) { if (avplay.open(stream_url)) { loaded = true; listener.send('canplay'); } } }; /** * Играть */ video.play = function () { if (loaded) { if (avplay.status == '5') { avplay.resume(); } else { avplay.play(function () { console.log('Player', 'play'); }, function (e) { listener.send('error', { error: 'code [' + e.code + '] ' + e.message }); }); } } }; /** * Пауза */ video.pause = function () { if (loaded) avplay.pause(); }; /** * Установить масштаб */ video.size = function (type) { changeScale(type); }; /** * Установить скорость */ video.speed = function (speed) { changeSpeed(speed); }; /** * Уничтожить */ video.destroy = function () { try { console.log('Player', 'destroy'); avplay.destroy(); } catch (e) {} $('body').toggleClass('orsay-player--show', false); video.remove(); listener.destroy(); }; webapis.avplay.getAVPlay(function (av) { avplay = av; }, function (e) { listener.send('error', { error: 'code [' + e.code + '] ' + e.message }); }); call_video(video); return object; } function YouTube$1(call_video) { var stream_url; var needclick = true; // Platform.screen('mobile') || navigator.userAgent.toLowerCase().indexOf('android') >= 0 var object = $("\n
    \n
    \n
    \n
    \n
    \n \n
    ".concat(Lang.translate('player_youtube_no_played'), "
    \n
    \n
    \n ")); var video = object[0]; var listener = Subscribe$2(); var volume = 100; var timeupdate; var timetapplay; var timer_waite; var frame = null; var frameWindow = null; var frameReady = false; var bridgeId = 'yt_' + Math.random().toString(36).slice(2); function getYouTubeId(url) { if (!url) return ''; try { url = String(url).trim(); if (url.includes('youtu.be/')) { return url.split('youtu.be/')[1].split(/[?&#/]/)[0]; } if (url.includes('youtube.com/embed/')) { return url.split('youtube.com/embed/')[1].split(/[?&#/]/)[0]; } if (url.includes('v=')) { return url.split('v=')[1].split('&')[0]; } return url; } catch (e) { return ''; } } function postToFrame(type) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; try { if (frameWindow) { frameWindow.postMessage({ bridgeId: bridgeId, type: type, data: data }, '*'); } } catch (e) {} } function videoSize() { var size = { width: 0, height: 0 }; var str = video._playbackQuality || ''; if (str === 'highres' || str === 'hd2160') { size.width = 3840; size.height = 2160; } else if (str === 'hd1440') { size.width = 2560; size.height = 1440; } else if (str === 'hd1080') { size.width = 1920; size.height = 1080; } else if (str === 'hd720') { size.width = 1280; size.height = 720; } else { size.width = 854; size.height = 480; } return size; } function onMessage(event) { if (!event.data || event.data.bridgeId !== bridgeId) return; var _event$data = event.data, type = _event$data.type, data = _event$data.data; if (type === 'bridgeReady') { frameReady = true; return; } if (type === 'ready') { listener.send('canplay'); listener.send('loadeddata'); clearInterval(timeupdate); timeupdate = setInterval(function () { if (video._playerState !== 2) listener.send('timeupdate'); }, 100); if (needclick) listener.send('playing'); return; } if (type === 'stateChange') { var state = data ? data.state : -1; video._playerState = state; object.removeClass('ended'); if (needclick) { object.find('.player-video__youtube-needclick div').text(Lang.translate('loading') + '...'); } if (state === 1) { listener.send('playing'); clearTimeout(timetapplay); if (needclick) { needclick = false; setTimeout(function () { object.find('.player-video__youtube-needclick').remove(); }, 500); } } if (state === 0) { object.addClass('ended'); listener.send('ended'); } if (state === 3) { listener.send('waiting'); } return; } if (type === 'time') { video._currentTime = data.currentTime || 0; video._duration = data.duration || 0; video._playerState = typeof data.playerState === 'number' ? data.playerState : video._playerState; video._playbackQuality = data.playbackQuality || ''; return; } if (type === 'qualityChange') { video._playbackQuality = data.quality || ''; console.log('YouTube', 'quality', video._playbackQuality); return; } if (type === 'error') { console.log('YouTube', 'error', data ? data.error : ''); object.find('.player-video__youtube-noplayed').removeClass('hide'); object.addClass('ended'); if (needclick) object.find('.player-video__youtube-needclick').remove(); clearTimeout(timetapplay); } } function createFrame(videoId) { if (frame) return; var url = object$2.github_lampa + 'youtube.html?bridgeId=' + bridgeId + '&videoId=' + videoId + '&autoplay=1&controls=1&mute=0&start=0'; frame = document.createElement('iframe'); frame.className = 'player-video__youtube-bridge-frame'; frame.src = url.toString(); frame.setAttribute('frameborder', '0'); frame.setAttribute('allowfullscreen', 'true'); frame.setAttribute('allow', 'autoplay; encrypted-media; picture-in-picture'); frame.style.border = '0'; frame.style.display = 'block'; frame.onload = function () { try { frameWindow = frame.contentWindow; } catch (e) {} }; object.find('.player-video__youtube-player').empty().append(frame); } Object.defineProperty(video, 'src', { set: function set(url) { if (url) stream_url = url; }, get: function get() {} }); Object.defineProperty(video, 'currentTime', { set: function set(t) { video._currentTime = t; postToFrame('seekTo', { time: t }); }, get: function get() { return video._currentTime || 0; } }); Object.defineProperty(video, 'duration', { set: function set() {}, get: function get() { return video._duration || 0; } }); Object.defineProperty(video, 'paused', { set: function set() {}, get: function get() { if (needclick) return true; return video._playerState === 2 || video._playerState == null; } }); Object.defineProperty(video, 'audioTracks', { set: function set() {}, get: function get() { return []; } }); Object.defineProperty(video, 'textTracks', { set: function set() {}, get: function get() { return []; } }); Object.defineProperty(video, 'videoWidth', { set: function set() {}, get: function get() { return videoSize().width; } }); Object.defineProperty(video, 'videoHeight', { set: function set() {}, get: function get() { return videoSize().height; } }); Object.defineProperty(video, 'volume', { set: function set(num) { volume = num * 100; postToFrame('setVolume', { volume: volume }); }, get: function get() {} }); video.canPlayType = function () { return true; }; video.resize = function () { postToFrame('resize'); }; video.addEventListener = listener.follow.bind(listener); video.load = function () { if (stream_url && !frame) { var id = getYouTubeId(stream_url); if (!id) { object.find('.player-video__youtube-noplayed').removeClass('hide').find('div').text(Lang.translate('player_youtube_no_support')); return; } video._currentTime = 0; video._duration = 0; video._playerState = -1; video._playbackQuality = ''; video.resize(); createFrame(id); if (needclick) { object.append("\n
    \n \n
    ").concat(Lang.translate('loading'), "...
    \n
    \n ")); timetapplay = setTimeout(function () { object.find('.player-video__youtube-needclick div').text(Lang.translate('player_youtube_start_play')); PlayerPanel.update('pause'); }, 10000); } var waitFrame = function waitFrame() { if (!frameReady) { timer_waite = setTimeout(waitFrame, 50); return; } postToFrame('init', { volume: volume }); }; waitFrame(); } }; video.play = function () { postToFrame('play'); }; video.pause = function () { postToFrame('pause'); }; video.size = function (type) {}; video.speed = function (speed) { postToFrame('setPlaybackRate', { rate: speed }); }; video.destroy = function () { clearInterval(timeupdate); clearTimeout(timetapplay); clearTimeout(timer_waite); postToFrame('destroy'); if (frame) { try { frame.remove(); } catch (e) {} } frame = null; frameWindow = null; frameReady = false; window.removeEventListener('message', onMessage); object.remove(); listener.destroy(); }; window.addEventListener('message', onMessage); call_video(video); return object; } var bell; var items$1 = []; function Item$1(params) { var _this = this; Arrays.extend(params, { icon: "\n \n \n ", type: 'info', text: '', from: '', time: 5000 }); var element = $("
    \n
    ").concat(params.icon, "
    \n
    ").concat(params.text, "
    \n
    ")); if (params.from) element.find('.bell__item-text').prepend("".concat(params.from, "
    ")); setTimeout(function () { element.removeClass('show'); setTimeout(function () { var slide = $("
    "); slide.css('height', element.outerHeight() + 'px'); element.after(slide); element.remove(); Arrays.remove(items$1, _this); slide.animate({ height: 0 }, 300, function () { slide.remove(); }); if (items$1.length == 0) { bell.removeClass('show'); } }, 300); }, params.time); bell.find('.bell__wrap').append(element); setTimeout(function () { element.addClass('show'); }, 100); items$1.push(this); Sound$1.play('bell'); } function init$14() { bell = $("
    \n
    \n
    "); $('body').append(bell); } function push$8(params) { new Item$1(params); bell.addClass('show'); } var Bell = { init: init$14, push: push$8 }; var listener$c = Subscribe$2(); var html$h; var display; var paused; var backworkIcon; var forwardIcon; var timer$6 = {}; var params$2 = {}; var rewind_position = 0; var rewind_force = 0; var _video; var retainedPipVideo = false; var wait; var need_scale; var need_scale_last; var need_speed; var _normalization; var click_nums = 0; var click_timer; var pause_timer; function init$13() { html$h = Template.get('player_video'); display = html$h.find('.player-video__display'); paused = html$h.find('.player-video__paused'); backworkIcon = html$h.find('.player-video__backwork-icon'); forwardIcon = html$h.find('.player-video__forward-icon'); html$h.find('.player-video__backwork-icon,.player-video__forward-icon').on('animationend', function () { $(this).removeClass('rewind'); }); html$h.on('click', function (e) { if (DeviceInput.canClick(e.originalEvent)) { clearTimeout(click_timer); click_nums++; if (PlayerIPTV.playning()) click_nums = 1; if (click_nums === 1) { click_timer = setTimeout(function () { click_nums = 0; if (PlayerPanel.visibleStatus() && !PlayerIPTV.playning()) playpause();else PlayerPanel.mousemove(); }, 300); } else if (click_nums > 1) { click_timer = setTimeout(function () { var third = window.innerWidth / 3; var pow = (click_nums - 1) * 10; var dir; if (e.clientX > third * 2) { dir = 1; } else if (e.clientX < third) { dir = -1; } if (dir == 1) { forwardIcon.addClass('rewind').find('span').text('+' + pow + ' sec'); to(_video.currentTime + dir * pow); } else if (dir == -1) { backworkIcon.addClass('rewind').find('span').text('-' + pow + ' sec'); to(_video.currentTime + dir * pow); } else if (Utils$1.canFullScreen()) { Utils$1.toggleFullscreen(); } click_nums = 0; }, 300); } } }); Lampa.Listener.follow('resize_end', function () { if (_video) { need_scale = need_scale_last; scale(); if (_video.resize) _video.resize(); } }); Tube.register({ name: 'YouTube', verify: function verify(src) { return src.indexOf('youtube.com') >= 0 || src.indexOf('youtu.be') >= 0; }, create: YouTube$1 }); Segments$1.listener.follow('skip', function (e) { if (Storage.get('player_segments_' + e.type, 'auto') == 'auto') { _video.currentTime = Math.min(_video.duration, e.segment.end); Bell.push({ text: Lang.translate('player_segments_skiped'), icon: Template.string('icon_viewed') }); } }); PlayerPanel.listener.follow('visible', function (e) { if (_normalization) _normalization.visible(e.status); }); } /** * Добовляем события к контейнеру */ function bind$3() { // ждем загрузки _video.addEventListener("waiting", function () { loader$1(true); }); // начали играть _video.addEventListener("playing", function () { loader$1(false); }); // видео закончилось _video.addEventListener('ended', function () { listener$c.send('ended', {}); }); if (Platform.is('apple') && Storage.field('player') == 'ios') { _video.addEventListener('webkitendfullscreen', function (e) { Controller.back(); }); } // что-то пошло не так _video.addEventListener('error', function (e) { var error = _video.error || {}; var msg = (error.message || '').toUpperCase(); if (msg.indexOf('EMPTY SRC') == -1) { if (error.code == 3) { listener$c.send('error', { error: Lang.translate('player_error_one'), fatal: true }); } else if (error.code == 4) { listener$c.send('error', { error: Lang.translate('player_error_two'), fatal: true }); } else if (typeof error.code !== 'undefined') { listener$c.send('error', { error: 'code [' + error.code + '] details [' + msg + ']', fatal: true }); } } }); // прогресс буферизации _video.addEventListener('progress', function (e) { if (typeof e.percent !== 'undefined') { listener$c.send('progress', { down: e.percent + '%' }); } else { var duration = _video.duration; var seconds = 0; if (duration > 0) { try { for (var i = 0; i < _video.buffered.length; i++) { if (_video.buffered.start && _video.buffered.start(_video.buffered.length - 1 - i) < _video.currentTime) { var down = Math.max(0, Math.min(100, _video.buffered.end(_video.buffered.length - 1 - i) / duration * 100)) + "%"; seconds = Math.max(0, _video.buffered.end(_video.buffered.length - 1 - i) - _video.currentTime); listener$c.send('progress', { down: down }); break; } } } catch (e) {} HlsStream.bitrate(seconds); } } }); // можно ли уже проигрывать? _video.addEventListener('canplay', function () { listener$c.send('canplay', {}); }); // сколько прошло _video.addEventListener('timeupdate', function () { if (rewind_position == 0 && !_video.rewind) listener$c.send('timeupdate', { duration: _video.duration, current: _video.currentTime }); listener$c.send('videosize', { width: _video.videoWidth, height: _video.videoHeight }); scale(); HlsStream.update(_video.currentTime); Segments$1.update(_video.currentTime); }); //получены первые данные _video.addEventListener('loadeddata', function (e) { Segments$1.adjust(_video.duration); listener$c.send('videosize', { width: _video.videoWidth, height: _video.videoHeight }); listener$c.send('loadeddata', { duration: _video.duration, current: _video.currentTime }); scale(); if (need_speed) speed(need_speed); loaded$2(); }); var pc = Boolean(Platform.desktop() || Platform.is('browser') || Platform.is('apple') && !Utils$1.isPWA()); // для страховки _video.volume = pc ? parseFloat(Storage.get('player_volume', '1')) : 1; _video.muted = false; } /** * Конвертировать object to array, * в некоторых случаях video возвращает object вместо array * @param {object[]} arr * @returns {array} */ function convertToArray(arr) { if (!Arrays.isArray(arr)) { var new_arr = []; for (var index = 0; index < arr.length; index++) { new_arr.push(arr[index]); } arr = new_arr; } return arr; } /** * Масштаб видео */ function scale() { if (!need_scale) return; var vw = _video.videoWidth, vh = _video.videoHeight, rt = 1, sx = 1.00, sy = 1.00; if (vw == 0 || vh == 0 || typeof vw == 'undefined') return; var increase = function increase(sfx, sfy) { rt = Math.min(window.innerWidth / vw, window.innerHeight / vh); sx = sfx; sy = sfy; }; if (need_scale == 'default') { rt = Math.min(window.innerWidth / vw, window.innerHeight / vh); } else if (need_scale == 'fill') { rt = Math.min(window.innerWidth / vw, window.innerHeight / vh); sx = window.innerWidth / (vw * rt); sy = window.innerHeight / (vh * rt); } else if (need_scale == 's115') { increase(1.15, 1.15); } else if (need_scale == 's130') { increase(1.34, 1.34); } else if (need_scale == 'v115') { increase(1.01, 1.15); } else if (need_scale == 'v130') { increase(1.01, 1.34); } else { rt = Math.min(window.innerWidth / vw, window.innerHeight / vh); vw = vw * rt; vh = vh * rt; rt = Math.max(window.innerWidth / vw, window.innerHeight / vh); sx = rt; sy = rt; } sx = sx.toFixed(2); sy = sy.toFixed(2); // Для некоторых платформ, где видео не масштабируется, а растягивается if (Platform.is('orsay') && Storage.field('player') == 'inner' || Storage.field('player_scale_method') == 'calculate') { var nw = vw * rt, nh = vh * rt; var sz = { width: Math.round(nw * sx) + 'px', height: Math.round(nh * sy) + 'px', marginLeft: Math.round(window.innerWidth / 2 - nw * sx / 2) + 'px', marginTop: Math.round(window.innerHeight / 2 - nh * sy / 2) + 'px' }; } else { var sz = { width: Math.round(window.innerWidth) + 'px', height: Math.round(window.innerHeight) + 'px', transform: sx == 1.00 && sy == 1.00 || need_scale == 'fill' ? 'unset' : 'scaleX(' + sx + ') scaleY(' + sy + ')', objectFit: need_scale == 'fill' ? 'fill' : 'contain' }; } $(_video).css(sz); need_scale = false; } /** * Сохранить текущие состояние дорожек и сабов * @returns {{sub:integer, track:integer, level:integer}} */ function saveParams() { var subs = _video.customSubs || _video.webos_subs || _video.textTracks || []; var tracks = []; var hlsTracks = HlsStream.audioTracks(); var dashTracks = DashStream.audioTracks(); var webosTracks = WebOSManager.audioTracks(); if (hlsTracks) tracks = hlsTracks;else if (dashTracks) tracks = dashTracks;else if (_video.audioTracks && _video.audioTracks.length) tracks = _video.audioTracks; if (webosTracks !== null) tracks = webosTracks; if (tracks.length) { for (var i = 0; i < tracks.length; i++) { if (tracks[i].enabled == true || tracks[i].selected == true) params$2.track = i; } } if (subs.length) { for (var _i = 0; _i < subs.length; _i++) { if (subs[_i].enabled == true || subs[_i].selected == true) { params$2.sub = subs[_i].index; } } } var hlsLevel = HlsStream.currentLevel(); var dashLevel = DashStream.currentLevel(); if (hlsLevel !== undefined) params$2.level = hlsLevel; if (dashLevel !== undefined) params$2.level = dashLevel; console.log('Player', 'saved params', params$2); return params$2; } /** * Очисить состояние */ function clearParamas() { params$2 = {}; } /** * Загрузитьновое состояние из прошлого * @param {{sub:integer, track:integer, level:integer}} saved_params */ function setParams(saved_params) { params$2 = saved_params; } /** * Смотрим есть ли дорожки и сабы */ function loaded$2() { var tracks = []; var subs = _video.customSubs || _video.textTracks || []; console.log('Player', 'video full loaded'); var hlsTracks = HlsStream.setupAudioTracks(); var dashTracks = DashStream.setupAudioTracks(); if (hlsTracks) tracks = hlsTracks;else if (dashTracks) tracks = dashTracks;else if (_video.audioTracks && _video.audioTracks.length) tracks = _video.audioTracks; console.log('Player', 'tracks', _video.audioTracks); if (WebOSManager.handleLoaded(params$2, subsview)) tracks = []; if (tracks.length) { tracks = convertToArray(tracks); if (typeof params$2.track !== 'undefined' && tracks[params$2.track]) { tracks.forEach(function (e) { e.selected = false; }); tracks[params$2.track].enabled = true; tracks[params$2.track].selected = true; console.log('Player', 'enable track by default'); } listener$c.send('tracks', { tracks: tracks }); } if (subs.length) { subs = convertToArray(subs); if (typeof params$2.sub !== 'undefined' && subs[params$2.sub]) { subs.forEach(function (e) { e.mode = 'disabled'; e.selected = false; }); subs[params$2.sub].mode = 'showing'; subs[params$2.sub].selected = true; subsview(true); } else if (Storage.field('subtitles_start')) { var code = Storage.field('subtitles_lang'); var pick = preferredSubtitle(subs, code == 'auto' ? Storage.get('language', 'ru') : code); if (pick) { pick.mode = 'showing'; pick.selected = true; } subsview(true); } listener$c.send('subs', { subs: subs }); } var hlsLevels = HlsStream.buildLevels(params$2.level); var dashLevels = DashStream.buildLevels(params$2.level); if (hlsLevels || dashLevels) listener$c.send('levels', hlsLevels || dashLevels); } /** * Включить или выключить субтитры * @param {boolean} status */ function subsview(status) { html$h.find('.player-video__subtitles').toggleClass('hide', !Boolean(status)); } /** * Создать контейнер для видео */ function create$9() { if (retainedPipVideo) { retainedPipVideo = false; WebOSManager.setup(); return; } var videobox; if (Platform.is('tizen') && Storage.field('player') == 'tizen') { videobox = AVPlay$1(function (object) { _video = object; }); } else if (Platform.is('orsay') && Storage.field('player') == 'orsay') { videobox = AVPlay(function (object) { _video = object; }); } else { videobox = $(''); if (Platform.is('apple') && Storage.field('player') !== 'ios') videobox.attr('playsinline', 'true'); _video = videobox[0]; if (typeof _video.canPlayType !== 'function') _video.canPlayType = function () {}; if (Storage.field('player_normalization')) { try { console.log('Player', 'normalization enabled'); _normalization = new Normalization(); _normalization.attach(_video); } catch (e) { console.log('Player', 'normalization error:', e.stack); } } } display.append(videobox); WebOSManager.setup(); bind$3(); } /** * Показать згразку или нет * @param {boolean} status */ function loader$1(status) { wait = status; html$h.toggleClass('video--load', status); } /** * Устанавливаем ссылку на видео * @param {string} src - ссылка на видео * @param {boolean} change_quality - указывает что меняем качество, нужно для hlsjs, чтобы не создавать парсер заново */ function url$3(src, change_quality) { loader$1(true); var verify = Tube.verify(src); if (verify) { var videobox = verify.create(function (object) { _video = object; }); !!videobox && display.append(videobox); bind$3(); setTimeout(function () { load$3(src); }, 100); return; } create$9(); if (/\.mpd/.test(src) && typeof dashjs !== 'undefined') { DashStream.create(src, _video, { load: load$3 }); } else if (/\.m3u8/.test(src)) { if (navigator.userAgent.toLowerCase().indexOf('maple') > -1) src += '|COMPONENT=HLS'; if (typeof Hls !== 'undefined') { var _HlsStream$shouldUseP = HlsStream.shouldUseProgram(_video, Player.playdata()), use_program = _HlsStream$shouldUseP.use_program, hls_native = _HlsStream$shouldUseP.hls_native; console.log('Player', 'use program hls:', use_program, 'hlsjs support:', Hls.isSupported()); if (!Platform.is('tizen')) console.log('Player', 'can play native hls:', hls_native ? true : false); if (use_program) { HlsStream.createProgram(src, _video, Player.playdata(), { play: play$2, load: load$3, error: function error(msg, fatal) { return listener$c.send('error', { error: msg, fatal: fatal }); }, subtitles: function subtitles(subs) { return listener$c.send('subs', { subs: subs }); } }); } else if (!change_quality && !PlayerIPTV.playning()) { HlsStream.createParser(src, Player.playdata(), { load: load$3, levels: function levels(_levels, current) { return listener$c.send('levels', { levels: _levels, current: current }); }, translate: function translate(where, _translate) { return listener$c.send('translate', { where: where, translate: _translate }); } }); } else load$3(src); } else load$3(src); } else load$3(src); } /** * Начать загрузку * @param {string} src */ function load$3(src) { HlsStream.destroyParser(); DashStream.destroy(); _video.src = src; console.log('Player', 'video load url:', src); _video.load(); play$2(); } /** * Играем */ function play$2() { try { var promise = _video.play(); var call = function call() { paused.addClass('hide'); listener$c.send('play', {}); console.log('Player', 'start playing'); }; if (promise && typeof promise.then === 'function') { promise.then(call)["catch"](function (e) { console.log('Player', 'play promise error:', e.message); }); } else { // Старые браузеры call(); } } catch (e) { console.log('Player', 'play error:', e.message); } } /** * Пауза */ function pause() { try { var promise = _video.pause(); var call = function call() { paused.removeClass('hide'); listener$c.send('pause', {}); console.log('Player', 'pause'); clearTimeout(pause_timer); pause_timer = setTimeout(function () { paused.addClass('hide'); }, 4000); }; if (promise && typeof promise.then === 'function') { promise.then(call)["catch"](function (e) { console.log('Player', 'pause promise error:', e.message); }); } else { // Старые браузеры call(); } } catch (e) { console.log('Player', 'pause error:', e.message); } } /** * Играем или пауза */ function playpause() { if (wait || rewind_position) return; if (_video.paused) play$2();else pause(); } /** * Завершаем перемотку * @param {boolean} immediately - завершить немедленно */ function rewindEnd(immediately) { clearTimeout(timer$6.rewind_call); timer$6.rewind_call = setTimeout(function () { _video.currentTime = rewind_position; rewind_position = 0; rewind_force = 0; play$2(); WebOSManager.rewinded(); }, immediately ? 0 : 1000); } /** * Подготовка к перемотке * @param {number} position_time - новое время * @param {boolean} immediately - завершить немедленно */ function rewindStart(position_time, immediately) { if (!_video.duration) return; rewind_position = Math.max(0, Math.min(position_time, _video.duration)); pause(); if (rewind_position == 0) _video.currentTime = 0;else if (rewind_position == _video.duration) _video.currentTime = _video.duration; timer$6.rewind = Date.now(); listener$c.send('timeupdate', { duration: _video.duration, current: rewind_position }); listener$c.send('rewind', {}); rewindEnd(immediately); } /** * Начать перематывать * @param {boolean} forward - направление, true - вперед * @param {number} custom_step - свое значение в секундах */ function rewind(forward, custom_step) { if (_video.duration) { var step = Storage.field('player_rewind'); if (rewind_position == 0) { rewind_force = Math.max(5, custom_step || step); rewind_position = _video.currentTime; } rewind_force *= 1.03; if (forward) { rewind_position += rewind_force; } else { rewind_position -= rewind_force; } var skip = Segments$1.get(_video.currentTime); if (forward && skip && !skip.segment.skiped && Storage.get('player_segments_' + skip.type) == 'user') { rewind_position = Math.min(_video.duration, skip.segment.end); skip.segment.skiped = true; } rewindStart(rewind_position); } } /** * Размер видео, масштаб * @param {string} type */ function size$1(type) { need_scale = type; need_scale_last = type; scale(); if (_video.size) _video.size(type); } function speed(value) { need_speed = value; var fv = value == 'default' ? 1 : parseFloat(value); if (_video.speed) _video.speed(fv);else if (WebOSManager.isActive()) WebOSManager.speed(fv);else _video.playbackRate = fv; } /** * Перемотка на позицию * @param {number} type */ function to(seconds) { pause(); try { if (seconds == -1) _video.currentTime = Math.max(0, _video.duration - 3);else _video.currentTime = Math.max(0, Math.min(seconds, _video.duration)); } catch (e) {} play$2(); } /** * Включить режим PIP */ function enterToPIP() { if (!document.pictureInPictureElement && document.pictureInPictureEnabled && _video.requestPictureInPicture) { _video.requestPictureInPicture(); } } /** * Выключить режим PIP */ function exitFromPIP() { if (document.pictureInPictureElement) { document.exitPictureInPicture(); } } /** * Переключить режим PIP */ function pip() { if (document.pictureInPictureElement) exitFromPIP();else enterToPIP(); } /** * Установить громкость * @param {number} vol 0-1 */ function volume(vol) { vol = Math.max(0, Math.min(vol, 1)); _video.volume = vol; Storage.set('player_volume', vol); } /** * Уничтожить * @param {boolean} savemeta - сохранить с параметрами * @param {boolean} keepPip - сохранить видеоэлемент при смене серии в PiP */ function destroy$c(savemeta, keepPip) { subsview(false); need_scale = false; paused.addClass('hide'); WebOSManager.destroy(); clearTimeout(click_timer); var hls_destoyed = HlsStream.destroy(); var dash_destoyed = DashStream.destroy(); if (!keepPip) exitFromPIP(); if (_video && !keepPip && !(hls_destoyed || dash_destoyed)) { if (_video.destroy) _video.destroy();else { _video.removeAttribute('src'); _video.load(); } } if (_normalization && !keepPip) { _normalization.destroy(); _normalization = false; } if (!keepPip) display.empty(); retainedPipVideo = Boolean(keepPip); loader$1(false); listener$c.send('destroy', { savemeta: savemeta }); } function render$b() { return html$h; } var PlayerVideo = { init: init$13, listener: listener$c, url: url$3, render: render$b, destroy: destroy$c, playpause: playpause, rewind: rewind, play: play$2, pause: pause, size: size$1, speed: speed, subsview: subsview, to: to, video: function video() { return _video; }, normalization: function normalization() { return _normalization; }, saveParams: saveParams, clearParamas: clearParamas, setParams: setParams, pip: pip, volume: volume, registerTube: Tube.register, removeTube: Tube.remove, verifyTube: Tube.verify }; var Nature = /*#__PURE__*/function () { function Nature() { _classCallCheck(this, Nature); this.slide = 'one'; this.direct = ['lt', 'rt', 'br', 'lb', 'ct']; } _createClass(Nature, [{ key: "create", value: function create() { var _this = this; this.html = Template.get('screensaver'); this.timer = function () { _this.next(); }; Timer.add(30000, this.timer); this.time = Utils$1.time(this.html); this.next(); } }, { key: "next", value: function next() { var _this2 = this; var image = 'https://picsum.photos/1600/900?v=' + Math.random(); var to_img = $('.screensaver__slides-' + (this.slide == 'one' ? 'two' : 'one'), this.html)[0]; to_img.onload = function () { $(to_img).removeClass(_this2.direct.join(' ') + ' animate').addClass(_this2.direct[Math.floor(Math.random() * _this2.direct.length)]); setTimeout(function () { $('.screensaver__slides-' + _this2.slide, _this2.html).removeClass('visible'); _this2.slide = _this2.slide == 'one' ? 'two' : 'one'; $(to_img).addClass('visible').addClass('animate'); }, 100); }; to_img.onerror = this.next.bind(this); to_img.src = image; } }, { key: "render", value: function render() { return this.html; } }, { key: "destroy", value: function destroy() { Timer.remove(this.timer); this.time.destroy(); this.html.remove(); } }]); return Nature; }(); var Chrome = /*#__PURE__*/function () { function Chrome() { _classCallCheck(this, Chrome); } _createClass(Chrome, [{ key: "create", value: function create() { this.html = $('
    '); } }, { key: "render", value: function render() { return this.html; } }, { key: "destroy", value: function destroy() { this.html.remove(); } }]); return Chrome; }(); var IndexedDB = /*#__PURE__*/function () { function IndexedDB(database_name) { var tables = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; var version = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 3; _classCallCheck(this, IndexedDB); this.listener = Subscribe$2(); this.database_name = 'lampa_' + database_name; this.tables = tables; this.version = version; this.db = null; this.logs = true; } _createClass(IndexedDB, [{ key: "log", value: function log(err, store_name, key) { if (this.logs) console.log('DB', this.database_name + (store_name ? '_' + store_name : '') + (key ? ' -> [' + key + ']' : ''), err); } /** * Открытие базы данных * @returns {Promise} */ }, { key: "openDatabase", value: function openDatabase() { var _this = this; return new Promise(function (resolve, reject) { var Base = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB; if (!Base) return _this.log('Not supported'), reject('Not supported'); if (!_this.tables.length) return _this.log('No tables'), reject('No tables'); var request = Base.open(_this.database_name, _this.version); request.onerror = function (event) { _this.log(request.error || 'An error occurred while opening the database'); reject(request.error || 'An error occurred while opening the database'); }; request.onsuccess = function (event) { _this.db = event.target.result; resolve(); }; request.onupgradeneeded = function (event) { var db = event.target.result; _this.log('OnUpgradeNeeded'); _this.tables.forEach(function (name) { if (!db.objectStoreNames.contains(name)) { _this.log('Create table - ' + name); db.createObjectStore(name, { keyPath: 'key' }); } }); }; }); } /** * Добавление данных в таблицу * @param {string} store_name - Название таблицы * @param {string} key - Ключ записи * @param {any} value - Значение записи * @returns {Promise} */ }, { key: "addData", value: function addData(store_name, key, value) { var _this2 = this; return new Promise(function (resolve, reject) { if (!_this2.db) { return _this2.log('Database not open', store_name, key), reject('Database not open'); } var transaction = _this2.db.transaction([store_name], 'readwrite'); var objectStore = transaction.objectStore(store_name); var addRequest = objectStore.add({ key: key, value: value, time: Date.now() }); addRequest.onerror = function (event) { _this2.log(addRequest.error || 'An error occurred while adding data', store_name, key); reject(addRequest.error || 'An error occurred while adding data'); }; addRequest.onsuccess = resolve; }); } /** * Получение данных из таблицы * @param {string} store_name - Название таблицы * @param {string} key - Ключ записи, если не указан, вернет все записи * @param {number} life_time - Время жизни записи в минутах. -1 - без ограничений * @param {boolean} return_meta - Возвращать мета данные (ключ, время) вместе со значением * @returns {Promise} */ }, { key: "getData", value: function getData(store_name, key) { var _this3 = this; var life_time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : -1; var return_meta = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; return new Promise(function (resolve, reject) { if (!_this3.db) { return _this3.log('Database not open', store_name, key), reject('Database not open'); } var transaction = _this3.db.transaction([store_name], 'readonly'); var objectStore = transaction.objectStore(store_name); var getRequest = key ? objectStore.get(key) : objectStore.getAll(); getRequest.onerror = function (event) { _this3.log(getRequest.error || 'An error occurred while retrieving data', store_name, key); reject(getRequest.error || 'An error occurred while retrieving data'); }; getRequest.onsuccess = function (event) { var result = event.target.result; if (result) { if (key) { if (life_time == -1) resolve(return_meta ? result : result.value);else { if (Date.now() < result.time + life_time * 1000 * 60) resolve(return_meta ? result : result.value);else resolve(null); } } else resolve(return_meta ? result : result.map(function (r) { return r.value; })); } else { resolve(null); } }; }); } /** * Получение данных из таблицы без ошибки * @param {string} store_name - Название таблицы * @param {string} key - Ключ записи * @param {number} life_time - Время жизни записи в минутах. -1 - без ограничений * @param {boolean} return_meta - Возвращать мета данные (ключ, время) вместе со значением * @returns {Promise} */ }, { key: "getDataAnyCase", value: function getDataAnyCase(store_name, key, life_time) { var _this4 = this; var return_meta = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; return new Promise(function (resolve, reject) { _this4.getData(store_name, key, life_time, return_meta).then(resolve)["catch"](function () { resolve(null); }); }); } /** * Обновление данных в таблице * @param {string} store_name - Название таблицы * @param {string} key - Ключ записи * @param {any} value - Новое значение записи * @returns {Promise} */ }, { key: "updateData", value: function updateData(store_name, key, value) { var _this5 = this; return new Promise(function (resolve, reject) { if (!_this5.db) { return _this5.log('Database not open', store_name, key), reject('Database not open'); } var transaction = _this5.db.transaction([store_name], 'readwrite'); var objectStore = transaction.objectStore(store_name); var getRequest = objectStore.get(key); getRequest.onerror = function (event) { _this5.log(getRequest.error || 'An error occurred while updating data', store_name, key); reject(getRequest.error || 'An error occurred while updating data'); }; getRequest.onsuccess = function (event) { var result = event.target.result; if (result) { result.value = value; result.time = Date.now(); var updateRequest = objectStore.put(result); updateRequest.onerror = function (event) { _this5.log(updateRequest.error || 'An error occurred while updating data', store_name, key); reject(updateRequest.error || 'An error occurred while updating data'); }; updateRequest.onsuccess = resolve; } else { _this5.log('No data found with the given key', store_name, key); reject('No data found with the given key'); } }; }); } /** * Перезапись данных в таблице (если нет, то создаст новую запись) * @param {string} store_name - Название таблицы * @param {string} key - Ключ записи * @param {any} value - Новое значение записи * @returns {Promise} */ }, { key: "rewriteData", value: function rewriteData(store_name, key, value) { var _this6 = this; return new Promise(function (resolve, reject) { if (!_this6.db) { return _this6.log('Database not open', store_name, key), reject('Database not open'); } var transaction = _this6.db.transaction([store_name], 'readwrite'); var objectStore = transaction.objectStore(store_name); var addRequest = objectStore.put({ key: key, value: value, time: Date.now() }); addRequest.onerror = function (event) { _this6.log(addRequest.error || 'An error occurred while rewrite data', store_name, key); reject(addRequest.error || 'An error occurred while rewrite data'); }; addRequest.onsuccess = resolve; }); } /** * Удаление данных из таблицы * @param {string} store_name - Название таблицы * @param {string} key - Ключ записи * @returns {Promise} */ }, { key: "deleteData", value: function deleteData(store_name, key) { var _this7 = this; return new Promise(function (resolve, reject) { if (!_this7.db) { return _this7.log('Database not open', store_name, key), reject('Database not open'); } var transaction = _this7.db.transaction([store_name], 'readwrite'); var objectStore = transaction.objectStore(store_name); var deleteRequest = objectStore["delete"](key); deleteRequest.onerror = function (event) { _this7.log(deleteRequest.error || 'An error occurred while deleting data', store_name, key); reject(deleteRequest.error || 'An error occurred while deleting data'); }; deleteRequest.onsuccess = resolve; }); } /** * Очистка таблицы * @param {string} store_name - Название таблицы * @returns {Promise} */ }, { key: "clearTable", value: function clearTable(store_name) { var _this8 = this; return new Promise(function (resolve, reject) { if (!_this8.db) { return _this8.log('Database not open', store_name), reject('Database not open'); } var transaction = _this8.db.transaction([store_name], 'readwrite'); var objectStore = transaction.objectStore(store_name); var clearRequest = objectStore.clear(); clearRequest.onerror = function (event) { _this8.log(clearRequest.error || 'An error occurred while clearing the table', store_name); reject(clearRequest.error || 'An error occurred while clearing the table'); }; clearRequest.onsuccess = function () { resolve(); }; }); } /** * Очистка всех таблиц * @returns {Promise} */ }, { key: "clearAll", value: function clearAll() { var _this9 = this; return new Promise(function (resolve, reject) { if (!_this9.db) { return _this9.log('Database not open'), reject('Database not open'); } var objectStoreNames = _this9.db.objectStoreNames; var tableNames = Array.from(objectStoreNames); tableNames.forEach(function (n) { _this9.clearTable(n); }); resolve(); }); } }]); return IndexedDB; }(); var Cache = new IndexedDB('cache', ['screensavers', 'plugins', 'backgrounds', 'images', 'themes', 'other', 'timetable', 'cards', 'storage'], 7); var Cub = /*#__PURE__*/function () { function Cub(params) { _classCallCheck(this, Cub); this.params = params; this["default"] = Utils$1.protocol() + object$2.cub_domain + '/img/background/default.mp4'; } _createClass(Cub, [{ key: "create", value: function create() { this.html = Template.get('screensaver'); var source = this.params && this.params.url ? this.params.url : Storage.get('cub_screensaver', ''); if (!source) source = this["default"]; this.url = Utils$1.fixMirrorLink(Utils$1.addUrlComponent(source, 'token=' + encodeURIComponent(Storage.get('account', '{}').token))); this.preload = $('
    '); this.html.prepend(this.preload); this.html.find('.screensaver__slides').remove(); this.time = Utils$1.time(this.html); this.time.tik(); if (Platform.is('webos')) this.video(this.url);else this.cache(this.url); } }, { key: "load", value: function load(video, er) { var _this = this; video.load(); var playPromise; try { playPromise = video.play(); } catch (e) {} var startPlay = function startPlay() { console.log('Screesaver', 'playing'); _this.preload.remove(); }; if (playPromise !== undefined) { playPromise.then(function () { return startPlay(); })["catch"](function (e) { console.log('Screesaver', 'play promise error:', e.message); }); } else startPlay(); } }, { key: "video", value: function video(src) { var video = $(''); this.html.prepend(video); video[0].src = src; this.load(video[0]); } }, { key: "cache", value: function cache(url) { var _this3 = this; var getblob = function getblob(result) { return new Promise(function (resolve, reject) { console.log('Screesaver', 'db find:', result ? 'true' : 'false'); if (result) resolve(result);else { console.log('Screesaver', 'start download video'); var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'blob'; xhr.onload = function (e) { var _this2 = this; if (this.status == 200) { Cache.addData('screensavers', url, this.response).then(function () { console.log('Screesaver', 'push blob to db'); resolve(_this2.response); })["catch"](resolve.bind(this.response)); } else reject(); }; xhr.onerror = reject; xhr.send(); } }); }; Cache.getData('screensavers', url).then(getblob).then(function (blob) { console.log('Screesaver', 'set video blob'); _this3.create_url_blob = URL.createObjectURL(new Blob([blob], { type: "video/mp4" })); _this3.video(_this3.create_url_blob); })["catch"](function (e) { console.log('Screesaver', 'error:', typeof e == 'string' ? e : e.message); _this3.video(url); }); } }, { key: "render", value: function render() { return this.html; } }, { key: "destroy", value: function destroy() { this.html.remove(); this.video = function () {}; if (this.create_url_blob) URL.revokeObjectURL(this.create_url_blob); } }]); return Cub; }(); var Aerial = /*#__PURE__*/function () { function Aerial(params) { _classCallCheck(this, Aerial); this.params = params; this.net = new Request(); this.visible = false; this.items = []; this.opacity = 0; this.transition_time = 4000; this.transition_timeout; } _createClass(Aerial, [{ key: "create", value: function create() { var _this = this; this.html = Template.get('screensaver'); this.preload = $('
    '); this.video = $('')[0]; this.overlay = $('
    '); this.video.addEventListener('timeupdate', function () { if (_this.video.duration) { var visible = _this.video.currentTime > 0 && _this.video.currentTime < _this.video.duration - 5; var points = _this.object.pointsOfInterest; if (visible !== _this.visible) { _this.visible = visible; _this.info.toggleClass('visible', visible); if (_this.visible) _this.fadeVideoOut(_this.transition_time);else _this.fadeVideoIn(_this.transition_time); } if (_this.video.currentTime == _this.video.duration && !_this.wait_load) { _this.wait_load = true; _this.select(); _this.play(); } if (points) { for (var time in points) { var text = points[time]; if (parseInt(time) <= _this.video.currentTime && _this.tagline.text_ready !== text) { _this.tagline.text_ready = text; _this.tagline.text(text); } } } } }); this.info = $("
    \n
    \n
    \n
    "); this.title = this.info.find('.screensaver__title-name'); this.tagline = this.info.find('.screensaver__title-tagline'); this.html.prepend(this.preload); this.html.prepend(this.overlay); this.html.prepend(this.video); this.html.find('.screensaver__slides').remove(); this.html.find('.screensaver__gradient').after(this.info); this.time = Utils$1.time(this.html); this.time.tik(); this.cache(function () { _this.select(); _this.play(); }); } }, { key: "select", value: function select() { this.object = this.items[Math.floor(Math.random() * this.items.length)]; this.title.text(this.object.name.replace(/\s\d$/, '')); this.tagline.text(Utils$1.capitalizeFirstLetter(this.object.type)); } }, { key: "fadeVideoIn", value: function fadeVideoIn(time) { if (time > 0) { this.transition_timeout = setTimeout(this.fadeVideoIn.bind(this), 16, time - 16); } this.opacity = 1 - time / this.transition_time; this.overlay[0].style.opacity = this.opacity; } }, { key: "fadeVideoOut", value: function fadeVideoOut(time) { if (time > 0) { this.transition_timeout = setTimeout(this.fadeVideoOut.bind(this), 16, time - 16); } this.opacity = time / this.transition_time; this.overlay[0].style.opacity = this.opacity; } }, { key: "play", value: function play() { var _this2 = this; this.video.src = this.object.src.H2641080p.replace('https:', 'http:'); this.video.load(); var playPromise; try { playPromise = this.video.play(); } catch (e) {} var startPlay = function startPlay() { console.log('Screesaver', 'playing'); _this2.preload.remove(); _this2.wait_load = false; }; if (playPromise !== undefined) { playPromise.then(function () { return startPlay(); })["catch"](function (e) { console.log('Player', 'play promise error:', e.message); }); } else startPlay(); } }, { key: "cache", value: function cache(call) { var _this3 = this; this.items = Storage.get('screensaver_aerial_items', '[]'); if (this.items.length) call();else { this.net.silent('https://raw.githubusercontent.com/OrangeJedi/Aerial/master/videos.json', function (json) { _this3.items = json.filter(function (a) { return a.src.H2641080p; }); Storage.set('screensaver_aerial_items', _this3.items); call(); }); } } }, { key: "render", value: function render() { return this.html; } }, { key: "destroy", value: function destroy() { this.html.remove(); this.net.clear(); clearTimeout(this.transition_timeout); } }]); return Aerial; }(); /** * Скринсейвер * @class * @returns {Screensaver} */ var Screensaver$1 = /*#__PURE__*/function () { function Screensaver() { _classCallCheck(this, Screensaver); this.listener = Subscribe$2(); this.enabled = false; this.worked = false; this.screensaver; this.timer; this.time_reset = 0; this.time_start = 0; this.class_list = { nature: Nature, chrome: Chrome, cub: Cub, aerial: Aerial }; } _createClass(Screensaver, [{ key: "init", value: function init() { var _this = this; this.html = $('
    '); this.html.on('click', this.stop.bind(this)); $('body').append(this.html); this.resetTimer(); Keypad.listener.follow('keydown', function (e) { _this.resetTimer(); if (_this.worked) { _this.stopSlideshow(); e.event.preventDefault(); } }); Keypad.listener.follow('keyup', function (e) { if (_this.worked) e.event.preventDefault(); }); $(window).on('mousedown', function (e) { _this.resetTimer(); }); $(window).on('focus', this.resetTimer.bind(this)); } }, { key: "toggle", value: function toggle(enabled) { this.enabled = enabled; this.resetTimer(); this.listener.send('toggle', { status: this.enabled }); } }, { key: "enable", value: function enable() { this.toggle(true); } }, { key: "disable", value: function disable() { this.toggle(false); } }, { key: "isWorked", value: function isWorked() { return this.enabled ? this.worked : this.enabled; } }, { key: "stop", value: function stop() { if (this.isWorked()) this.stopSlideshow(); } }, { key: "show", value: function show(type, params) { clearTimeout(this.timer); this.listener.send('start', {}); var select = Storage.field('screensaver_type'); if (typeof type == 'string') select = type; var Class = Chrome; if (this.class_list[select]) Class = this.class_list[select]; this.screensaver = new Class(params); this.screensaver.create(); this.html.append(this.screensaver.render()); this.html.fadeIn(300); this.worked = true; } }, { key: "resetTimer", value: function resetTimer() { var _this2 = this; clearTimeout(this.timer); this.time_reset = Date.now(); if (!Storage.field('screensaver') || !this.enabled || this.worked) return; var timeout = 1000 * 60 * Storage.field('screensaver_time'); this.timer = setTimeout(function () { //для ведра, когда в лампе появляетя фокус срабатывает таймер if (Date.now() - _this2.time_reset <= timeout + 100) _this2.show();else _this2.resetTimer(); }, timeout); } }, { key: "stopSlideshow", value: function stopSlideshow() { var _this3 = this; this.worked = false; this.html.fadeOut(300, function () { _this3.html.removeClass('visible'); if (_this3.screensaver) { _this3.screensaver.destroy(); _this3.screensaver = false; } }); this.resetTimer(); this.listener.send('stop', {}); } }]); return Screensaver; }(); var Screensaver$2 = new Screensaver$1(); function languageCode(value) { return String(value || '').toLowerCase().split(/[-_]/)[0]; } function selectLogo(logos, language) { if (!Array.isArray(logos) || !logos.length) return false; var preferred = languageCode(language); var ordered = [preferred, 'en', null].filter(function (code, index, list) { return code !== '' && list.indexOf(code) === index; }); var _loop = function _loop(i) { var code = ordered[i]; var logo = logos.find(function (item) { return code === null ? !item.iso_639_1 : languageCode(item.iso_639_1) === code; }); if (logo && logo.file_path) return { v: logo }; }; for (var i = 0; i < ordered.length; i++) { var _ret = _loop(i); if (_typeof(_ret) === "object") return _ret.v; } return logos.find(function (item) { return item.file_path; }) || false; } function imageUrl(path, size) { if (!path) return ''; if (/^(https?:)?\/\//i.test(path) || /^(data|blob):/i.test(path)) return path; return Api.img(path, size); } function mediaData() { var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var card = data.card || data.movie || data; var logo = selectLogo(card.images && card.images.logos, Storage.field('tmdb_lang')); return { title: data.first_title || card.title || card.name || data.title || '', background: imageUrl(card.backdrop_path, 'original') || card.background_image || card.background || data.background || data.img || data.thumbnail || '', logo: logo ? imageUrl(logo.file_path, 'w500') : card.logo || data.logo || '' }; } function createMark(media, class_name) { var mark = $('
    '); if (media.logo) { mark.append($('').attr('src', media.logo)); } else { mark.append($('
    ').text(media.title)); } return mark; } function create$8() { var html = $("
    \n \"\"\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n \n \n \n \n \n \n 0%\n
    \n
    \n
    "); var media = {}; var hide_timer; function set(data) { media = mediaData(data); var backdrop = html.find('.media-loading__backdrop'); var background = html.find('.media-loading__mark-background'); var fill = html.find('.media-loading__mark-fill'); if (media.background) backdrop.attr('src', media.background).removeClass('hide');else backdrop.attr('src', '').addClass('hide'); background.empty().append(createMark(media, 'media-loading__mark-content--background')); fill.empty().append(createMark(media, 'media-loading__mark-content--foreground')); html.toggleClass('media-loading--text', !media.logo); } function update(value, detail) { var progress = Math.max(0, Math.min(100, parseFloat(value) || 0)); var stats = _typeof(detail) == 'object' && detail ? detail : { speed: detail }; var speed = stats.speed || ''; var active = parseInt(stats.active_peers); var total = parseInt(stats.total_peers); var show_peers = !isNaN(active); html.find('.media-loading__mark-fill').css('width', progress + '%'); html.find('.media-loading__percent').text(Math.round(progress) + '%'); html.find('.media-loading__speed').text(speed).toggleClass('hide', !speed); html.find('.media-loading__peers-separator').toggleClass('hide', !show_peers); html.find('.media-loading__speed-separator').toggleClass('hide', !speed); html.find('.media-loading__peers').toggleClass('hide', !show_peers); html.find('.media-loading__peers-value').text(show_peers ? active + ' / ' + (isNaN(total) ? '—' : total) : ''); } function show(data) { clearTimeout(hide_timer); if (data) set(data); html.removeClass('hide media-loading--leaving'); } function hide(immediate) { clearTimeout(hide_timer); if (immediate) { html.addClass('hide').removeClass('media-loading--leaving'); } else { html.addClass('media-loading--leaving'); hide_timer = setTimeout(function () { html.addClass('hide').removeClass('media-loading--leaving'); }, 300); } } function destroy() { clearTimeout(hide_timer); html.remove(); } return { render: function render() { return html; }, set: set, update: update, show: show, hide: hide, destroy: destroy }; } var MediaLoading = { create: create$8 }; var callback_cancel, controller_enabled, loader, media_loader, timer$5; /** * Отображает слой загрузки с возможностью отмены * @param {function} on_cancel - вызывается при отмене загрузки * @param {string} text - текст загрузки * @returns {void} */ function start$9(on_cancel, text) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; callback_cancel = on_cancel; controller_enabled = Controller.enabled().name; if (media_loader) { media_loader.destroy(); media_loader = false; } if (options.media) { media_loader = MediaLoading.create(); loader = media_loader.render().addClass('media-loading--standalone'); media_loader.set(options.media); media_loader.update(options.progress || 0, text); media_loader.show(); } else { loader = $("
    \n
    \n
    ".concat(Lang.translate('loading'), "
    \n
    \n
    \n
    ")); if (text) loader.find('.loading-layer__text').text(text); } loader.on('click', function (e) { if (DeviceInput.canClick(e.originalEvent)) cancel(); }); clearTimeout(timer$5); if (options.media) { $('body').append(loader); } else { timer$5 = setTimeout(function () { $('body').append(loader); }, 500); } toggle$7(); } /** * Переключает контроллер на слой загрузки * @returns {void} */ function toggle$7() { Controller.add('loading', { invisible: true, toggle: function toggle() { Controller.clear(); }, back: cancel, up: cancel, down: cancel, left: cancel, right: cancel }); Controller.toggle('loading'); } /** * Вызывает колбэк отмены загрузки * @returns {void} */ function cancel() { if (callback_cancel) callback_cancel(); } /** * Удаляет слой загрузки и восстанавливает контроллер * @returns {void} */ function stop$1() { if (media_loader) { media_loader.destroy(); media_loader = false; } else if (loader) loader.remove(); loader = false; clearTimeout(timer$5); if (controller_enabled) Controller.toggle(controller_enabled); } /** * Обновляет текст загрузки * @param {string} text - текст загрузки * @returns {void} */ function setText(text) { if (media_loader) media_loader.update(0, text);else if (loader) loader.find('.loading-layer__text').text(text); } function setProgress(progress, detail) { if (media_loader) media_loader.update(progress, detail);else setText(Math.round(progress) + '%' + (detail ? ' - ' + detail : '')); } var Loading$1 = { start: start$9, stop: stop$1, setText: setText, setProgress: setProgress }; function init$12() { Settings.listener.follow('open', function (e) { if (e.name == 'account') e.body.find('.settings--account-device-add').on('hover:enter', function () { login(function () { Controller.toggle('settings_component'); }); }); }); } function add$c(value) { var code = parseInt(value); if (!isNaN(code)) { var _login = function _login(error) { network.silent(Api$1.url() + 'device/add', function (result) { Loading$1.stop(); Storage.set('account', result, true); Storage.set('account_email', result.email, true); window.location.reload(); }, error, { code: code }); }; var network = new Request(); Loading$1.start(function () { network.clear(); Loading$1.stop(); }); _login(function (e) { if (network.errorCode(e) == 200) { Loading$1.stop(); Noty.show(Lang.translate('account_code_error')); } else { localStorage.setItem('protocol', window.location.protocol == 'https:' ? 'https' : 'http'); _login(function (e) { Loading$1.stop(); Noty.show(Lang.translate(network.errorCode(e) == 200 ? 'account_code_error' : 'network_noconnect')); }); } }); } else { Noty.show(Lang.translate('account_code_wrong')); } } function login(callback) { var html = Template.get('account_add_device_new'); var nums = html.find('.account-modal-split__code-num'); var keyboard; if (Platform.tv()) { var code = html.find('.account-modal-split__qr-code'); var img = html.find('.account-modal-split__qr-img'); html.addClass('layer--' + (Platform.mouse() ? 'wheight' : 'height')); Utils$1.qrcode('https://' + object$2.cub_site + '/add', code, function () { code.remove(); img.removeClass('hide'); Utils$1.imgLoad(img, Utils$1.protocol() + object$2.qr_device_add, function () { img.addClass('loaded'); }); }); } else html.addClass('account-modal-split--mobile'); function drawCode(value) { nums.find('span').text('-'); value.split('').forEach(function (v, i) { if (nums[i]) nums[i].find('span').text(v); }); } drawCode(''); Modal.open({ title: '', html: html, size: Platform.tv() ? 'full' : 'medium', scroll: { nopadding: true }, onBack: function onBack() { keyboard.destroy(); Modal.close(); callback && callback(); } }); keyboard = new window.SimpleKeyboard["default"]({ display: { '{BKSP}': ' ', '{ENTER}': ' ' }, layout: { 'default': ['0 1 2 3 4 {BKSP}', '5 6 7 8 9 {ENTER}'] }, onChange: function onChange(value) { drawCode(value); if (value.length == 6) add$c(value); }, onKeyPress: function onKeyPress(button) { if (button === '{BKSP}') { keyboard.setInput(keyboard.getInput().slice(0, -1)); drawCode(keyboard.getInput()); } else if (button === '{ENTER}') { if (keyboard.getInput().length == 6) add$c(keyboard.getInput()); } } }); var keys = $('.simple-keyboard .hg-button').addClass('selector'); Controller.collectionSet($('.simple-keyboard')); Controller.collectionFocus(keys[0], $('.simple-keyboard')); $('.simple-keyboard .hg-button').on('hover:enter', function (e) { Controller.collectionFocus($(this)[0]); keyboard.handleButtonClicked($(this).attr('data-skbtn'), e); }); } var Device = { init: Utils$1.onceInit(init$12), login: login }; function show$8(template_name) { var enabled = Controller.enabled().name; Modal.open({ title: '', html: Template.get(template_name), onBack: function onBack() { Modal.close(); Controller.toggle(enabled); } }); } function account() { var enabled = Controller.enabled().name; var html = Template.js('account_none'); if (Platform.tv()) { var code = html.find('.account-modal-split__qr-code'); var img = html.find('.account-modal-split__qr-img'); html.addClass('layer--' + (Platform.mouse() ? 'wheight' : 'height')); Utils$1.qrcode('https://' + object$2.cub_site + '/#signup', code, function () { code.remove(); img.removeClass('hide'); Utils$1.imgLoad(img, Utils$1.protocol() + object$2.qr_site, function () { img.addClass('loaded'); }); }); } else html.addClass('account-modal-split--mobile'); Modal.open({ title: '', html: $(html), size: Platform.tv() ? 'full' : 'medium', scroll: { nopadding: true }, onSelect: function onSelect() { Modal.close(); Device.login(function () { Controller.toggle(enabled); }); }, onBack: function onBack() { Modal.close(); Controller.toggle(enabled); } }); } function limited() { show$8('account_limited'); } function premium() { var enabled = Controller.enabled().name; var html = Template.js('account_premium'); if (Platform.tv()) { html.addClass('layer--' + (Platform.mouse() ? 'wheight' : 'height')); Utils$1.qrcode('https://' + object$2.cub_site + '/premium', html.find('.account-modal-split__qr-code'), function () { html.find('.account-modal-split__qr').remove(); }); } else html.addClass('account-modal-split--mobile'); if (!permit.token) { var button = Template.elem('div', { "class": 'simple-button simple-button--inline selector', text: Lang.translate('settings_cub_signin_button') }); button.on('hover:enter', function () { Modal.close(); Controller.toggle(enabled); account(); }); html.find('.account-modal-split__info').append(button); } Modal.open({ title: '', html: $(html), size: Platform.tv() ? 'full' : 'medium', scroll: { nopadding: true }, onBack: function onBack() { Modal.close(); Controller.toggle(enabled); } }); } var Modal$1 = { account: account, limited: limited, premium: premium }; var network$9 = new Request(); function url$2() { return Utils$1.protocol() + object$2.cub_domain + '/api/'; } function load$2(path) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var post = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; return new Promise(function (resolve, reject) { if (permit.token) { var account = permit.account; Arrays.extend(params, { headers: { token: account.token, profile: account.profile.id }, timeout: 8000 }); var u = params.url ? params.url : url$2() + path; network$9.silent(u, resolve, reject, post, params); } else { reject({ decode_code: 403 }); } }); } function persons(secuses, error) { if (permit.access && !window.lampa_settings.disable_features.persons) { load$2('person/list').then(function (data) { Storage.set('person_subscribes_id', data.results.map(function (a) { return a.person_id; })); if (secuses) secuses(data.results); })["catch"](error ? error : function () {}); } else if (error) error({ decode_code: 403 }); } function user(secuses, error) { if (permit.access) { load$2('users/get?device_name=' + decodeURIComponent(Storage.get('device_name'))).then(function (data) { Storage.set('account_user', JSON.stringify(data.user)); if (secuses) secuses(data.user); })["catch"](error ? error : function () {}); } else if (error) error({ decode_code: 403 }); } function plugins(call) { if (permit.access) { load$2('plugins/all', { timeout: 3000 }).then(function (result) { if (result.secuses) { Storage.set('account_plugins', result.plugins); call(result.plugins); } else { call(Storage.get('account_plugins', '[]')); } })["catch"](function () { call(Storage.get('account_plugins', '[]')); }); } else call([]); } function pluginToggle(plugin, status) { if (permit.access) { load$2((plugin.author ? 'extensions' : 'plugins') + '/status', {}, { id: plugin.id, status: status })["catch"](function () {}); } } function notices(call) { if (permit.access) { load$2('notice/all', { cache: 1000 * 60 * 10 }).then(function (result) { if (result.secuses) { Storage.set('account_notice', result.notice.map(function (n) { return n; })); call(result.notice); } else call([]); })["catch"](function () { call([]); }); } else call([]); } function subscribes() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var secuses = arguments.length > 1 ? arguments[1] : undefined; var error = arguments.length > 2 ? arguments[2] : undefined; if (permit.access) { load$2('notifications/all').then(function (result) { if (params.to_card_subscribe) { var cards = []; result.notifications.forEach(function (n) { var card = Arrays.decodeJson(n.card, {}); card.subscribe = n; delete card.subscribe.card; cards.push(card); }); secuses({ results: cards }); } else { secuses({ results: result.notifications.map(function (r) { return Arrays.decodeJson(r.card, {}); }) }); } })["catch"](error ? error : function () {}); } else if (error) error({ decode_code: 403 }); } function subscribeToTranslation() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var call = arguments.length > 1 ? arguments[1] : undefined; var error = arguments.length > 2 ? arguments[2] : undefined; if (permit.access && params.voice) { load$2('notifications/add', {}, { voice: params.voice, data: JSON.stringify(Utils$1.clearCard(params.card)), episode: params.episode, season: params.season }).then(function (result) { if (result.limited) Modal$1.limited();else if (call) call(); })["catch"](error ? error : function () {}); } else if (error) error({ decode_code: 403 }); } var Api$1 = { url: url$2, load: load$2, user: user, persons: persons, plugins: plugins, pluginToggle: pluginToggle, subscribes: subscribes, notices: notices, subscribeToTranslation: subscribeToTranslation, clear: network$9.clear }; /** * Трекер синхронизации * @class * @param {string} name - имя трекера * @returns {Tracker} */ var Tracker = /*#__PURE__*/function () { function Tracker(name) { _classCallCheck(this, Tracker); this.name = name; } /** * Получить трекеры синхронизации * @return {object} - {time, version} */ _createClass(Tracker, [{ key: "get", value: function get() { return Storage.get(this.name + '_' + this.profile(), JSON.stringify({ time: 0, version: 0 })); } /** * Сохранить трекеры синхронизации * @param {object} data - {time, version} * @return {void} */ }, { key: "set", value: function set(data) { Storage.set(this.name + '_' + this.profile(), data); } /** * Обновить трекеры синхронизации * @param {object} data - {time, version} * @return {void} */ }, { key: "update", value: function update(data) { var traker = this.get(); traker.time = data.time; traker.version = data.version; this.set(traker); } /** * Получить ID профиля * @returns {number} */ }, { key: "profile", value: function profile() { return permit.token ? permit.account.profile.id : 0; } /** * Получить версию последней синхронизации * @returns {number} */ }, { key: "version", value: function version() { return this.get().version || 0; } /** * Получить время последней синхронизации * @returns {number} */ }, { key: "time", value: function time() { return this.get().time || 0; } }]); return Tracker; }(); var tracker = new Tracker('account_timeline_sync'); function init$11() { Storage.listener.follow('change', function (e) { if (e.name == 'account_use' || e.name == 'account') refrash(); }); Socket.listener.follow('open', function () { if (Date.now() - window.app_time_end > 1000 * 60 * 5) update$b(); }); Socket.listener.follow('send', function (e) { if (e.method == 'timeline') send$1(e.data); }); Storage.listener.follow('clear', function () { refrash(); }); } /** * Отправить прогресс просмотра на сервер * @param {object} data - данные для отправки * @param {object} data.params - параметры прогресса * @returns {void} */ function send$1(data) { if (!permit.sync) return; // Отправляем только данные. Сюда часто приходит объект из Timeline.view() вместе с функцией // handler, а jQuery.param при сборке запроса вызывает найденные функции: handler() без // аргументов перезаписывает прогресс с percent: undefined, и отметка тут же слетает. var params = JSON.parse(JSON.stringify(data.params)); Api$1.load('timeline/update', {}, params).then(function (result) {})["catch"](function (e) { console.log('Account', 'timeline send error', e); }); } /** * Обновить трекер и таймлайн, если сменился профиль или вошли в аккаунт * @returns {void} */ function refrash() { tracker.update({ version: 0, time: 0 }); update$b(); } /** * Обновить таймлайн * @returns {void} */ function update$b() { if (permit.sync) { // Если с момента последнего обновления прошло больше 10 дней, то загружаем дамп if (tracker.time() < Date.now() - 1000 * 60 * 60 * 24 * 10) { console.log('Account', 'timeline start full update', tracker.version()); Api$1.load('timeline/dump', { dataType: 'text' }).then(function (result) { // Парсим текст в массив AppWorker.json({ type: 'parse', data: result }, function (e) { var data = e.data; var name = 'file_view_' + permit.account.profile.id; if (!data.timelines) { return console.error('Account', 'timeline wrong dump format, no timelines:', result); } console.log('Account', 'timeline dump loaded, items:', Object.keys(data.timelines).length, 'profile id:', permit.account.profile.id); // Если нет файла в localStorage, то создаем его из кеша if (window.localStorage.getItem(name) === null) { Storage.set(name, JSON.stringify(Storage.cache('file_view', 10000, {}))); } var viewed = Storage.cache(name, 10000, {}); for (var i in data.timelines) { var time = data.timelines[i]; viewed[i] = time; Arrays.extend(viewed[i], { duration: 0, time: 0, percent: 0, updated: 0 }); delete viewed[i].hash; } Storage.set(name, viewed, false, function () { // Наверно закончилось место в localStorage, тогда сбрасываем версию на следующее обновление tracker.update({ version: 0, time: 0 }); console.warn('Account', 'timeline dump error, not saved to storage, try again next update'); }); tracker.update({ version: data.version, time: Date.now() }); Timeline$1.read(); // Нужно прочитать прогресс просмотра из localStorage console.log('Account', 'timeline dump update complete to version', data.version); }); })["catch"](function () { console.error('Account', 'timeline dump error, not loaded'); }); } // Иначе получаем только изменения с последней версии else { console.log('Account', 'timeline start update since', tracker.version()); Api$1.load('timeline/changelog?since=' + tracker.version()).then(function (result) { console.log('Account', 'timeline changelog loaded, items:', Object.keys(result.timelines || {}).length, 'profile id:', permit.account.profile.id); for (var i in result.timelines) { var time = result.timelines[i]; time.received = true; // Чтоб снова не отправлять и не зациклить Timeline$1.update(time); } tracker.update({ version: result.version || tracker.version(), time: Date.now() }); console.log('Account', 'timeline update since complete to version', tracker.version()); })["catch"](function (e) { console.warn('Account', 'timeline update changelog error, no response', e); }); } } // Если вышли из аккаунта, то повторно прочитываем прогресс просмотра из localStorage else Timeline$1.read(); } var Timeline$2 = { init: Utils$1.onceInit(init$11), update: update$b }; var rows = []; var component$o = 'content_rows'; function init$10() { SettingsApi.addComponent({ component: component$o, after: 'interface', icon: "\n \n \n \n ", name: Lang.translate('title_channels') }); Lampa.Settings.listener.follow('open', function (e) { if (e.name == 'main') settings(); }); } function settings() { SettingsApi.removeParams(component$o); rows.filter(function (r) { return r.name; }).forEach(function (row) { var name = component$o + '_' + (row.name || 'unknown'); SettingsApi.addParam({ component: component$o, param: { name: name, type: 'trigger', "default": true }, field: { name: row.title || Lang.translate('extensions_no_name') } }); }); } function add$b(row) { if (_typeof(row) == 'object' && typeof row.call == 'function') { if (rows.indexOf(row) == -1) { rows.push(row); if (!row.name || !row.title) { console.warn('Content row must have name and title', row); } } } else { console.warn('Content row must be an object with a call function', row); } } function remove$3(row) { var index = rows.indexOf(row); if (index > -1) { rows.splice(index, 1); } } function call$1(screen, params, calls) { var stop = ['genres', 'keywords']; if (stop.find(function (a) { return params[a]; })) return; rows.filter(function (row) { return row.screen ? Arrays.isArray(row.screen) ? row.screen.indexOf(screen) >= 0 : row.screen == screen : false; }).filter(function (row) { return Storage.get(component$o + '_' + (row.name || 'unknown'), 'true'); }).forEach(function (row) { var result = row.call(params, screen); if (Arrays.isArray(result)) { result.forEach(function (callback, i) { if (typeof callback == 'function') { Arrays.insert(calls, (row.index || 0) + i, callback); } else if (Arrays.isObject(callback)) { Arrays.insert(calls, (row.index || 0) + i, callback); } }); } else if (Arrays.isObject(result)) { Arrays.insert(calls, row.index || 0, result); } else if (typeof result == 'function') { Arrays.insert(calls, row.index || 0, result); } }); } var ContentRows = { init: init$10, add: add$b, remove: remove$3, call: call$1 }; var broken_images = 0; function proxy(name) { var proxy = Storage.field(name); if (proxy.length > 0 && proxy.charAt(proxy.length - 1) == '/') { proxy = proxy.substring(0, proxy.length - 1); } return Utils$1.checkHttp(proxy); } /** * Проксировать API запрос * @param {string} url URL запроса * @return {string} Проксированный URL запроса */ function api$2(url) { var base = Utils$1.protocol() + 'api.themoviedb.org/3/' + url; return Storage.field('proxy_tmdb') && Storage.field('tmdb_proxy_api') ? proxy('tmdb_proxy_api') + '/' + base : base; } /** * Проксировать изображение * @param {string} url URL изображения * @return {string} Проксированный URL изображения */ function image(url) { var base = Utils$1.protocol() + 'image.tmdb.org/' + url; return Storage.field('proxy_tmdb') && Storage.field('tmdb_proxy_image') ? proxy('tmdb_proxy_image') + '/' + base : base; } /** * Сообщить о сломанных изображениях */ function broken() { broken_images++; if (broken_images > 50) { broken_images = 0; if (Storage.field('tmdb_proxy_image') && Storage.field('proxy_tmdb_auto')) Storage.set('proxy_tmdb', true); } } /** * Получить ключ TMDB * @return {string} Ключ TMDB */ function key() { return '4ef0d7355d9ffb5151e987764708ce96'; } var TMDB$1 = { api: api$2, key: key, image: image, broken: broken }; var NoticeClass = /*#__PURE__*/function () { function NoticeClass() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, NoticeClass); this.params = params; this.name = 'Noname'; } _createClass(NoticeClass, [{ key: "active", value: function active() { return true; } }, { key: "push", value: function push(element, resolve, reject) { resolve(); } }, { key: "empty", value: function empty(title, descr) { var item = Template.get('notice_card', { title: title || '', descr: descr || Lang.translate('notice_none_system'), time: '' }); item.addClass('image--icon image--loaded'); item.find('.notice__img').html(Template.string('icon_bell_plus')); return item; } }, { key: "viewed", value: function viewed() {} }, { key: "items", value: function items() { return []; } }, { key: "count", value: function count() { return 0; } }]); return NoticeClass; }(); var NoticeAll = /*#__PURE__*/function (_NoticeClass) { _inherits(NoticeAll, _NoticeClass); var _super = _createSuper(NoticeAll); function NoticeAll() { var _this; var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, NoticeAll); _this = _super.call(this, params); _this.name = Lang.translate('settings_param_jackett_interview_all'); return _this; } _createClass(NoticeAll, [{ key: "active", value: function active() { return false; } }, { key: "count", value: function count() { return Notice$1.count(); } }, { key: "viewed", value: function viewed() { for (var name in Notice$1.classes) { if (Notice$1.classes[name] !== this) { Notice$1.classes[name].viewed(); } } } }, { key: "items", value: function items() { var _this2 = this; var items = []; var _loop = function _loop(name) { if (Notice$1.classes[name] !== _this2) { items = items.concat(Notice$1.classes[name].items().map(function (item) { var new_item = Arrays.clone(item); new_item.display = name; return new_item; })); } }; for (var name in Notice$1.classes) { _loop(name); } items.sort(function (a, b) { return a.time > b.time ? -1 : a.time < b.time ? 1 : 0; }); return items; } }]); return NoticeAll; }(NoticeClass); var NoticeLampa = /*#__PURE__*/function (_NoticeClass) { _inherits(NoticeLampa, _NoticeClass); var _super = _createSuper(NoticeLampa); function NoticeLampa() { var _this; var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, NoticeLampa); _this = _super.call(this, params); _this.name = params.name || 'Lampa'; _this.time = 0; _this.view = 0; _this.db_name = params.db_name || 'notice'; _this.notices = []; _this.connect(); return _this; } _createClass(NoticeLampa, [{ key: "connect", value: function connect() { this.db = new IndexedDB(this.db_name, ['all', 'readed'], 2); this.db.openDatabase().then(this.update.bind(this))["catch"](function () { return console.log('Notice', 'error', 'no open database'); }); } }, { key: "update", value: function update() { var _this2 = this; this.db.getData('readed', 'time').then(function (time) { _this2.time = time || 0; return _this2.db.getData('all'); }).then(function (result) { result.sort(function (a, b) { return a.time > b.time ? -1 : a.time < b.time ? 1 : 0; }); _this2.notices = result; _this2.view = result.filter(function (n) { return n.time > _this2.time; }).length; Notice$1.drawCount(); })["catch"](function (e) {}); } }, { key: "count", value: function count() { return this.view; } }, { key: "push", value: function push(element, resolve, reject) { if (!(element.id && element.from)) { if (reject) reject('No (id) or (from)'); return; } if (!this.notices.find(function (n) { return n.id == element.id; })) { this.db.addData('all', element.id, element).then(this.update.bind(this)).then(function (e) { if (resolve) resolve(e); })["catch"](function (e) { if (reject) reject(e); }); } else if (reject) reject('Already added'); } }, { key: "viewed", value: function viewed() { this.db.rewriteData('readed', 'time', Date.now()); this.view = 0; this.time = Date.now(); Notice$1.drawCount(); } }, { key: "items", value: function items() { return this.notices; } }]); return NoticeLampa; }(NoticeClass); function enrichTmdbNotice(element, oncomplite) { var card = element.card || {}; if (!card.imdb_id) return oncomplite(element); var method = 'find/' + card.imdb_id + '?external_source=imdb_id'; Api.sources.tmdb.get(method, {}, function (data) { var movie = data.movie_results && data.movie_results[0]; var tv = data.tv_results && data.tv_results[0]; var tmdb = card.seasons ? tv || movie : movie || tv; if (tmdb) { tmdb.source = 'tmdb'; tmdb.imdb_id = card.imdb_id; tmdb.imdb_rating = card.imdb_rating; tmdb.kp_rating = card.kp_rating; tmdb.release_quality = card.release_quality; tmdb.countries = card.countries; tmdb.seasons = card.seasons; tmdb.episode = card.episode; element.card = tmdb; element.title = tmdb.title || tmdb.name || element.title; element.poster = tmdb.poster_path || element.poster; } oncomplite(element); }, function () { oncomplite(element); }, { life: 60 * 24 * 7 }); } function enrichTmdbNotices(items, oncomplite) { var result = []; var index = 0; var active = 0; var limit = 4; function next() { if (index >= items.length && active == 0) return oncomplite(result); var _loop = function _loop() { var position = index; var element = items[index]; index++; active++; enrichTmdbNotice(element, function (updated) { result[position] = updated; active--; next(); }); }; while (active < limit && index < items.length) { _loop(); } } next(); } var NoticeCub = /*#__PURE__*/function (_NoticeClass) { _inherits(NoticeCub, _NoticeClass); var _super = _createSuper(NoticeCub); function NoticeCub() { var _this; var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, NoticeCub); _this = _super.call(this, params); _this.name = 'CUB'; _this.time = Storage.get('cub_notice_time', '0'); _this.notices = []; Cache.getData('other', 'cub_notice').then(function (data) { if (data && permit.sync) _this.notices = data; })["catch"](function (e) {}); Storage.listener.follow('change', function (e) { if (e.name == 'account' || e.name == 'account_use') { if (!permit.sync) _this.notices = [];else _this.update(); } }); Timer.add(1000 * 60 * 5, _this.update.bind(_assertThisInitialized(_this))); _this.update(); return _this; } _createClass(NoticeCub, [{ key: "update", value: function update() { var _this2 = this; Account$1.Api.notices(function (result) { var notices = result.map(function (item) { var data = JSON.parse(item.data); var text = Lang.translate('notice_new_quality'); var labels = []; if (data.card.seasons) { var k = []; for (var i in data.card.seasons) { k.push(i); } var s = k.pop(); labels.push('S - ' + s + ''); labels.push('E - ' + data.card.seasons[s] + ''); if (data.voice) labels.push(data.voice); text = Lang.translate('notice_new_episode'); } else { labels.push(Lang.translate('notice_quality') + ' - ' + data.card.quality + ''); } return { time: item.time || Utils$1.parseToDate(item.date).getTime(), title: !Lang.selected(['ru', 'uk', 'be']) ? data.card.original_title || data.card.original_name : data.card.title || data.card.name, text: text, poster: data.card.poster ? data.card.poster : data.card.img ? data.card.img : data.card.poster_path, card: data.card, labels: labels, data: data, item: item }; }); notices.sort(function (a, b) { return a.time > b.time ? -1 : a.time < b.time ? 1 : 0; }); enrichTmdbNotices(notices, function (items) { _this2.notices = items; Cache.rewriteData('other', 'cub_notice', _this2.notices); Notice$1.drawCount(); }); }); } }, { key: "viewed", value: function viewed() { Storage.set('cub_notice_time', Date.now()); this.time = Date.now(); Notice$1.drawCount(); } }, { key: "empty", value: function empty() { var item = _get(_getPrototypeOf(NoticeCub.prototype), "empty", this).call(this, Lang.translate('empty_title_two'), Lang.translate('notice_none_account')); if (!Account$1.Permit.access) { item = _get(_getPrototypeOf(NoticeCub.prototype), "empty", this).call(this, Lang.translate('account_none_title'), Lang.translate('notice_none')); if (Platform.screen('tv')) Utils$1.qrcode('https://' + object$2.cub_site, item.find('.notice__img')); return item; } return item; } }, { key: "count", value: function count() { var _this3 = this; return this.notices.filter(function (n) { return n.time > _this3.time; }).length; } }, { key: "items", value: function items() { return this.notices; } }]); return NoticeCub; }(NoticeClass); function translate$1(str) { if (Arrays.isObject(str)) { var code = Lampa.Storage.get('language', 'ru'); if (str[code]) return str[code];else if (str.ru) return str.ru;else return str[Arrays.getKeys(str)[0]]; } return str; } var Notice = /*#__PURE__*/function () { function Notice() { _classCallCheck(this, Notice); this.listener = Subscribe$2(); this.display = 'all'; this.classes = {}; } _createClass(Notice, [{ key: "init", value: function init() { var _this = this; this.icon = Head.addIcon(Template.string('icon_bell'), this.open.bind(this)); this.icon.addClass('notice--icon'); this.classes.all = new NoticeAll(); this.classes.lampa = new NoticeLampa(); this.classes.cub = new NoticeCub(); Lampa.Listener.follow('app', function (e) { if (e.type == 'ready') _this.drawCount(); }); } }, { key: "get", value: function get(name) { if (!this.classes[name]) throw new Error('No find class: ' + name); return this.classes[name]; } }, { key: "open", value: function open() { var _this2 = this; if (Lampa.Controller.enabled().name == 'modal') Modal.close(); var html = $('
    '); var tabs = []; var viever = this.classes[this.display]; var items = viever.items(); var navigation = $(''); for (var name in this.classes) { var count = this.classes[name].count(); var tab = { name: name, count: count }; tabs.push(tab); } tabs.forEach(function (tab, i) { var button = $(''); if (tab.count) button.append('' + tab.count + ''); button.on('hover:enter', function () { _this2.display = tab.name; _this2.open(); }); if (tab.name == _this2.display) button.addClass('active'); if (i > 0) navigation.append(''); navigation.append(button); }); html.append(navigation); items.forEach(function (element) { try { var _element$card; var item = Template.get('notice_card', {}); var icon = element.poster || ((_element$card = element.card) === null || _element$card === void 0 ? void 0 : _element$card.poster_path) || element.icon || element.img; var author_data = {}; var author_html; item.addClass('image--' + (element.poster ? 'poster' : element.icon ? 'icon' : element.img ? 'img' : 'none')); item.find('.notice__title').html(translate$1(element.title)); item.find('.notice__descr').html(translate$1(element.text)); item.find('.notice__time').html(Utils$1.parseTime(element.time)["short"]); if (element.labels) item.find('.notice__descr').append($('')); if (element.author) { author_data = translate$1(element.author); author_html = $("
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    "); author_html.find('.notice__author-name').html(author_data.name); author_html.find('.notice__author-text').html(author_data.text); item.find('.notice__body').append(author_html); } item.on('hover:enter', function () { if (element.card) { _this2.close(); Activity.push({ url: '', component: 'full', id: element.card.id, method: element.card.number_of_seasons || element.card.seasons ? 'tv' : 'movie', card: element.card, source: element.card.source || (Lang.selected(['ru', 'uk', 'be']) ? 'cub' : '') }); } else _this2.listener.send('select', { display: element.display || _this2.display, element: element }); }).on('visible', function () { if (icon) { var _element$card2; if (Arrays.isObject(icon)) icon = icon[Lampa.Storage.get('tmdb_lang')] || ((_element$card2 = element.card) === null || _element$card2 === void 0 ? void 0 : _element$card2.poster_path); if (icon && icon.indexOf('http') == -1) icon = TMDB$1.image('t/p/w300/' + icon); var img_icon = item.find('.notice__left img')[0] || {}; var img_author = item.find('.notice__author img')[0] || {}; img_icon.onload = function () { item.addClass('image--loaded'); }; img_icon.onerror = function () { img_icon.src = './img/img_broken.svg'; }; img_author.onload = function () { item.addClass('image-author--loaded'); }; img_author.onerror = function () { img_author.src = './img/img_broken.svg'; }; img_icon.src = icon ? Utils$1.fixProtocolLink(icon) : './img/img_broken.svg'; if (element.author) img_author.src = author_data.img ? Utils$1.fixProtocolLink(author_data.img.indexOf('http') >= 0 ? author_data.img : TMDB$1.image('t/p/w200/' + author_data.img)) : './img/img_broken.svg'; } }); html.append(item); } catch (e) { console.error('Notice', 'render error', e, element); } }); if (!items.length) { var empty = viever.empty(); if (typeof empty == 'string') { var wrap = $('
    '); wrap.append(empty); html.append(wrap); } else html.append(empty); } viever.viewed(); this.listener.send('viewed', { display: this.display }); Modal.open({ title: Lang.translate('title_notice'), select: html.find('.navigation-tabs .active')[0], size: 'medium', html: html, onBack: this.close.bind(this) }); } }, { key: "count", value: function count() { var all = 0; for (var name in this.classes) { if (this.classes[name].active()) all += this.classes[name].count(); } return all; } }, { key: "close", value: function close() { Modal.close(); Controller.toggle('head'); } }, { key: "drawCount", value: function drawCount() { var _this3 = this; var status = Boolean(this.count()); this.icon.toggleClass('active', status); clearInterval(this.blick_timer); if (status) { this.blick_timer = setInterval(function () { _this3.icon.addClass('animate'); setTimeout(function () { _this3.icon.removeClass('animate'); }, 1000); }, 1000 * 15); } } }, { key: "addClass", value: function addClass(class_name, noticeClass) { this.classes[class_name] = noticeClass; } }, { key: "pushNotice", value: function pushNotice(class_name, data, resolve, reject) { if (this.classes[class_name] && this.classes[class_name].push) { this.classes[class_name].push(data, resolve, reject); } else if (reject) reject('No find class'); } }]); return Notice; }(); var Notice$1 = new Notice(); /** * Утилита для отслеживания статуса * Нужно для того, чтобы дождаться завершения нескольких асинхронных процессов * @constructor * @param {number} need - количество процессов, которые нужно дождаться * @property {function} onComplite - функция, которая будет вызвана по завершении всех процессов * @property {function} stop - остановить проверку (например, если пользователь ушёл со страницы) * @property {function(string, object)} append - добавить успешный процесс * @property {function()} error - отметить ошибку в процессе * @example let status = new Status(3) status.onComplite = (result)=>{ //все процессы завершены } asyncProcess1((data)=>{ status.append('process1', data) }, ()=>{ status.error() }) asyncProcess2((data)=>{ status.append('process2', data) }, ()=>{ status.error() }) asyncProcess3((data)=>{ status.append('process3', data) }, ()=>{ status.error() }) */ function Status(need) { this.data = {}; this.work = 0; this.need = need; this.complited = false; this.check = function () { if (this.stopped) return; if (this.work >= this.need && !this.complited) { this.complited = true; this.onComplite(this.data); } }; this.append = function (name, json) { this.work++; this.data[name] = json; this.check(); }; this.error = function () { this.work++; this.check(); }; this.stop = function () { this.stopped = true; }; } function hasOpaqueBackground(el) { var bg = getComputedStyle(el).backgroundColor; // Если background-color: transparent if (bg === 'transparent') return false; // Если rgba(0, 0, 0, 0) или любая с alpha = 0 var match = bg.match(/rgba?\(([^)]+)\)/); if (match) { var parts = match[1].split(',').map(function (p) { return p.trim(); }); var alpha = parts[3] !== undefined ? parseFloat(parts[3]) : 1; return alpha > 0; } // Если это rgb(...) или hex — значит непрозрачный return true; } /** * Плашка для отображения дополнительной информации или действий. * @extends Emit * @param {object} params - параметры плашки * @param {string} [params.text] - текст плашки * @param {string} [params.style=default] - стиль плашки (default, accent, warning, danger) * @returns {More} - экземпляр класса More */ var More$1 = /*#__PURE__*/function (_Emit) { _inherits(More, _Emit); var _super = _createSuper(More); function More() { var _this; var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, More); _this = _super.call(this); _this.params = params; return _this; } _createClass(More, [{ key: "create", value: function create() { this.html = Template.js('more'); this.html.find('.card-more__title').html(this.params.text || Lang.translate('more')); this.params.style && this.html.addClass('card-more--' + (this.params.style || 'default')); this.emit('create'); } }, { key: "size", value: function size(target) { if (!target) return this.html.find('.card-more__box').style.height = '10em'; var elements = [target].concat(Array.from(target.querySelectorAll('*'))); var max_area = 0; var biggest_element = null; elements.forEach(function (el) { var rect = el.getBoundingClientRect(); var area = rect.width * rect.height; if (area > max_area && hasOpaqueBackground(el)) { max_area = area; biggest_element = el; } }); if (biggest_element) { var rect = biggest_element.getBoundingClientRect(); this.html.style.width = (rect.width > rect.height ? rect.height : rect.width) + 'px'; this.html.find('.card-more__box').style.height = rect.height + 'px'; this.html.addClass('card-more--fixed-size'); } } }, { key: "render", value: function render(js) { return js ? this.html : $(this.html); } }, { key: "destroy", value: function destroy() { this.html.remove(); this.emit('destroy'); } }]); return More; }(Emit); var MoreFirst = { onCreate: function onCreate() { var _this = this; this.more = new More$1(this.params.more); this.more.create(); this.more.html.addClass('card-more--first'); this.more.html.on('hover:focus hover:touch', function () { _this.last = _this.more.render(true); _this.active = _this.items.indexOf(_this.more); _this.scroll.update(_this.more.render(true), _this.params.items.align_left ? false : true); }); this.more.html.on('hover:enter', this.emit.bind(this, 'more', this.data)); this.scroll.append(this.more.render(true)); this.items.push(this.more); }, onAppend: function onAppend(item, elem) { if (this.data.results.indexOf(elem) == 0) this.more_set_size = item; }, onVisible: function onVisible() { var _this$more_set_size; this.more.size((_this$more_set_size = this.more_set_size) === null || _this$more_set_size === void 0 ? void 0 : _this$more_set_size.render(true)); this.more_set_size = null; } }; var Items$2 = { onInit: function onInit() { this.tv = Platform.screen('tv'); this.items = []; this.active = 0; this.view = this.params.items.view; }, onAppend: function onAppend(item, element) { var _this = this; var render = item.render(true); render.on('hover:focus', function () { _this.last = render; var prev_active = _this.active; _this.active = _this.items.indexOf(item); if (_this.active > 0 || prev_active > _this.active) _this.scroll.update(_this.items[_this.active].render(true), _this.params.items.align_left ? false : true); _this.emit('active', item, element); }); render.on('hover:touch', function () { _this.last = render; _this.active = _this.items.indexOf(item); _this.emit('active', item, element); }); render.on('hover:enter', function () { _this.last = render; }); render.on('visible', function () { if (Controller.own(_this)) Controller.collectionAppend(render); }); if (element.params.on && _typeof(element.params.on) == 'object') { var _loop = function _loop(e) { render.on(e, function () { element.params.on[e].call(_this, item, element); }); }; for (var e in element.params.on) { _loop(e); } } this.scroll.append(render); this.items.push(item); this.emit('push', item, element); }, onCreate: function onCreate() { this.scroll.body(true).addClass('mapping--' + this.params.items.mapping); this.scroll.onScroll = this.emit.bind(this, 'scroll'); this.data.results.slice(0, this.view).forEach(this.emit.bind(this, 'createAndAppend')); }, onScroll: function onScroll() { var size = this.tv ? (Math.round(this.active / this.view) + 1) * this.view + 1 : this.data.results.length; var start = this.items.length; if (this.has(MoreFirst)) start -= 1; this.data.results.slice(start, size).forEach(this.emit.bind(this, 'createAndAppend')); Layer.visible(this.scroll.render(true)); }, onDestroy: function onDestroy() { Arrays.destroy(this.items); } }; var Base$2 = /*#__PURE__*/function (_Emit) { _inherits(Base, _Emit); var _super = _createSuper(Base); function Base(data) { var _this; _classCallCheck(this, Base); _this = _super.call(this); Arrays.extend(data, { title: data.name, original_title: data.original_name, release_date: data.first_air_date, params: {} }); Arrays.extend(data.params, { style: { name: 'default' } }); _this.data = data; _this.params = data.params; return _this; } _createClass(Base, [{ key: "create", value: function create() { this.html = document.createElement('div'); this.emit('create'); } }, { key: "visible", value: function visible() { this.emit('visible'); } }, { key: "update", value: function update() { this.emit('update'); } }, { key: "render", value: function render(js) { return js ? this.html : $(this.html); } }, { key: "disable", value: function disable() { var status = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; this.disabled = status; this.html.toggleClass('card--disabled', status); this.emit('disable', status); } }, { key: "destroy", value: function destroy() { this.html.remove(); this.emit('destroy'); } }]); return Base; }(Emit); var Card$3 = /*#__PURE__*/function (_Base) { _inherits(Card, _Base); var _super = _createSuper(Card); function Card(data) { var _this; _classCallCheck(this, Card); _this = _super.call(this, data); var module = _this.params.module || Helper$9.MASK.base; Helper$9.getNames(module).map(function (name) { return CardMap[name]; }).forEach(function (mod) { return _this.use(mod); }); _this.emit('init'); return _this; } return Card; }(Base$2); var Create$2 = { onCreateAndAppend: function onCreateAndAppend(element) { try { var item = Utils$1.createInstance(Card$3, element); this.emit('instance', item, element); item.create(); this.emit('append', item, element); } catch (e) { console.warn('Warning', 'onCreateAndAppend error:', e.message, e.stack); } } }; var More = { onVisible: function onVisible() { var pages = this.data.total_pages || 1; if (pages <= 1) return; var button = document.createElement('div'); button.classList.add('items-line__more'); button.classList.add('selector'); button.text(Lang.translate('more')); button.on('hover:enter', this.emit.bind(this, 'more', this.data)); this.html.find('.items-line__head').append(button); }, onScroll: function onScroll() { var _this = this; if (!this.more && this.data.results.length == this.items.length && this.data.total_pages > 1) { this.more = new More$1(this.params.more); this.more.create(); this.more.size(this.items[this.items.length - 1].render(true)); this.more.html.on('hover:focus hover:touch', function () { _this.last = _this.more.render(true); _this.active = _this.items.indexOf(_this.more); _this.scroll.update(_this.more.render(true), _this.params.items.align_left ? false : true); }); this.more.html.on('hover:enter', this.emit.bind(this, 'more', this.data)); this.scroll.append(this.more.render(true)); this.items.push(this.more); if (Controller.own(this)) Controller.collectionAppend(this.more.render(true)); } } }; var Event$1 = { onEvent: function onEvent(type) { Lampa.Listener.send('line', { line: this, type: type, params: this.params, data: this.data, scroll: this.scroll, body: this.body, items: this.items, active: this.active }); }, onCreate: function onCreate() { this.emit('event', 'create'); }, onCreateAndAppend: function onCreateAndAppend() { this.emit('event', 'append'); }, onVisible: function onVisible() { this.emit('event', 'visible'); }, onToggle: function onToggle() { this.emit('event', 'toggle'); }, onDestroy: function onDestroy() { this.emit('event', 'destroy'); } }; var Icon$1 = { onCreate: function onCreate() { var _this = this; var icon = $("
    \n
    \n \n
    \n \n
    \n
    ".concat(this.data.title, "
    \n
    \n
    "))[0]; icon.on('visible', function () { if (_this.data.icon_svg) { icon.find('.full-person__photo').html(_this.data.icon_svg); if (_this.data.icon_bgcolor) icon.find('.full-person__photo').style.backgroundColor = _this.data.icon_bgcolor; if (_this.data.icon_color) icon.find('.full-person__photo').style.color = _this.data.icon_color; icon.addClass('full-person--loaded'); icon.addClass('full-person--svg'); } else { Utils$1.imgLoad(icon.find('img'), _this.data.icon_img || './img/actor.svg', function (img) { icon.addClass('full-person--loaded'); }, function (img) { img.src = './img/actor.svg'; }); } }); this.html.find('.items-line__title').html(icon); } }; var LineMap = { MoreFirst: MoreFirst, Items: Items$2, Create: Create$2, More: More, Icon: Icon$1, Event: Event$1 }; var Helper$b = new Mask(Arrays.getKeys(LineMap)); Helper$b.MASK.base = Helper$b.except('Icon', 'MoreFirst'); function ImageMirror() { var stat = []; var mirrors = ['imagetmdb.com/', 'nl.imagetmdb.com/', 'de.imagetmdb.com/', 'pl.imagetmdb.com/', 'lampa.byskaz.ru/tmdb/img/']; if (Account$1.hasPremium()) Arrays.insert(mirrors, 0, 'imagetmdb.' + object$2.cub_domain + '/'); mirrors.forEach(function (mirror) { stat[mirror] = { errors: [], banned: false }; }); this.current = function () { var free = mirrors.filter(function (mirror) { return !stat[mirror].banned; }); var last = Storage.get('tmdb_img_mirror', ''); if (free.indexOf(last) > -1) { return last; } else if (free.length) { Storage.set('tmdb_img_mirror', free[0]); return free[0]; } return free.length ? free[0] : mirrors[0]; }; this.broken = function (url) { mirrors.forEach(function (mirror) { if (url && url.indexOf('://' + mirror) !== -1) { var now = Date.now(); var s = stat[mirror]; s.errors.push(now); s.errors = s.errors.filter(function (t) { return now - t < 10000; }); console.log('TMDB-Proxy', 'mirror', mirror, 'errors', s.errors.length); if (s.errors.length >= 20) { s.banned = true; s.errors = []; console.warn('TMDB-Proxy', 'mirror', mirror, 'banned'); } } }); }; } function init$$() { var tmdb_proxy = { name: 'TMDB Proxy', version: '1.0.6', description: 'Проксирование постеров и API сайта TMDB', path_api: 'apitmdb.' + object$2.cub_domain + '/3/', path_api_backup: 'lampa.byskaz.ru/tmdb/api/3/' }; var IMG = new ImageMirror(); function filter(u) { var s = u.slice(0, 8); var e = u.slice(8).replace(/\/+/g, '/'); return s + e; } function email() { return Storage.get('account', '{}').email || ''; } TMDB$1.image = function (url) { var base = Utils$1.protocol() + 'image.tmdb.org/' + url; return Utils$1.addUrlComponent(filter(Storage.field('proxy_tmdb') ? Utils$1.protocol() + IMG.current() + url : base), 'email=' + encodeURIComponent(email())); }; TMDB$1.api = function (url) { var base = Utils$1.protocol() + 'api.themoviedb.org/3/' + url; return Utils$1.addUrlComponent(filter(Storage.field('proxy_tmdb') ? Utils$1.protocol() + tmdb_proxy.path_api + url : base), 'email=' + encodeURIComponent(email())); }; TMDB$1.broken = function (url) { IMG.broken(url); }; Settings.listener.follow('open', function (e) { if (e.name == 'tmdb') { e.body.find('[data-parent="proxy"]').remove(); } }); console.log('TMDB-Proxy', 'init'); console.log('TMDB-Proxy', Storage.field('proxy_tmdb') ? 'enabled' : 'disabled'); } var TMDBProxy = { init: init$$ }; /** * Короче, постоянно пишут (почему нет картинок?) * Решил сделать автоматическую установку TMDB Proxy если регион RU */ var network$8 = new Request(); var responce_code = 'ru'; function region(call) { if (!window.lampa_settings.geo) return call && call(responce_code); var reg = Storage.get('region', '{}'); Arrays.extend({ time: 0 }); if (!reg.code || reg.time + 1000 * 60 * 60 * 24 < Date.now()) { var extracted = function extracted(code) { code = code.trim().toLowerCase(); code = code.length <= 2 ? code || 'ru' : 'ru'; Storage.set('region', { code: code, time: Date.now() }); call(code); }; extract$1(extracted, function (e, x) { console.warn('VPN', 'geo.' + object$2.cub_domain + ' domain not responding', network$8.errorDecode(e, x)); Storage.set('region', { code: Storage.field('language'), time: Date.now() }); call(Storage.field('language')); }); } else call(reg.code); } var extract$1 = function extract(call, error) { $.ajax({ url: Utils$1.protocol() + 'geo.' + object$2.cub_domain, type: 'GET', dataType: 'text', timeout: 8000, success: call, error: error }); }; function task$4(call) { if (!window.lampa_settings.geo) return call && call(); extract$1(function (country) { console.log('VPN', 'geo.' + object$2.cub_domain + ' domain responding ', country); country = country.trim().toLowerCase(); if (country.length > 10) { country = 'ru'; console.warn('VPN', 'wrong responce, use default ru'); } if ((country == 'ru' || country == 'by' || country == '' || country.length > 10) && !window.lampa_settings.disable_features.install_proxy) { console.log('VPN', 'launch TMDB Proxy'); TMDBProxy.init(); } responce_code = country || 'ru'; // Для совместимости с Manifest.cub_domain window.vpn_region = responce_code; call(); }, function (e, x) { console.warn('VPN', 'geo.' + object$2.cub_domain + ' domain not responding:', network$8.errorDecode(e, x)); if (!window.lampa_settings.disable_features.install_proxy) { console.log('VPN', 'launch TMDB Proxy'); TMDBProxy.init(); //будем считать что если не ответил, то все равно запускаем } call(); }); } function is$1() { var need_array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; return need_array.indexOf(responce_code) >= 0; } var vpn = { task: task$4, is: is$1 }; Object.defineProperty(vpn, 'region', { value: function value(call) { region(call); }, writable: false }); Object.defineProperty(vpn, 'code', { value: function value() { return responce_code; }, writable: false }); var lgbt = ["bisexual man", "bisexuality", "black lgbt", "closeted homosexual", "drag queen", "female homosexuality", "gay hardcore", "gay adoption", "gay artist", "gay bar", "gay club", "gay couple", "gay erotica", "gay for pay", "gay friend", "gay history", "gay interest", "gay kiss", "gay love", "gay love story", "gay man", "gay marriage", "gay men", "gay muslim", "gay parent", "gay pornography", "gay pride", "gay priest", "gay relationship", "gay rights", "gay romance", "gay sauna", "gay sex", "gay son", "gay teenager", "gay theme", "gay youth", "gender transition", "homoerotic", "homoeroticism", "homoerotism", "homofobia", "homophobia", "homophobic", "homophobic attack", "homosexual", "homosexual father", "homosexuality", "indigenous lgbt", "intersex", "intersexuality", "lesbian", "lesbian couple", "lesbian love", "lesbian maid", "lesbian nun", "lesbian relationship", "lesbian sex", "lgbt", "lgbt activist", "lgbt athlete", "lgbt history", "lgbt in africa", "lgbt in the military", "lgbt parenting", "lgbt rights", "lgbt teen", "male homosexuality", "queer", "queer cinema", "queer history", "queer loneliness", "queer love", "queer romance", "queer sexuality", "repressed gay", "teenage lesbian romance", "trans man", "trans woman", "transgender", "transgender rights", "transition", "transsexual", "transsexuality", "transvestism", "transvestite", "boys' love", "boys love"]; var adult = ['porn', 'sex', 'xxx', 'erotic', 'fetish', 'bdsm', 'bondage', 'sado', 'masochism', 'voyeurism', 'exhibitionism', 'incest', 'softcore', 'eroticism', 'erotica', 'nude', 'nudity', 'nudism', 'naked', 'pink film']; var filter$2 = ['путин', 'россия', 'putin', 'russia', 'лукашенко', 'lukashenko', 'навальный', 'navalny', '^буча', 'мариуполь', 'гомосекс', 'homosex', 'кремль']; var Keys = { lgbt: lgbt, adult: adult, filter: filter$2 }; var network$7 = new Request(); var menu_list = []; var day$1 = 60 * 24; var source$2 = 'tmdb'; var genres$1 = { movie: [{ "id": 28, "title": "#{filter_genre_ac}" }, { "id": 12, "title": "#{filter_genre_ad}" }, { "id": 16, "title": "#{filter_genre_mv}" }, { "id": 35, "title": "#{filter_genre_cm}" }, { "id": 80, "title": "#{filter_genre_cr}" }, { "id": 99, "title": "#{filter_genre_dc}" }, { "id": 18, "title": "#{filter_genre_dr}" }, { "id": 10751, "title": "#{filter_genre_fm}" }, { "id": 14, "title": "#{filter_genre_fe}" }, { "id": 36, "title": "#{filter_genre_hi}" }, { "id": 27, "title": "#{filter_genre_ho}" }, { "id": 10402, "title": "#{filter_genre_mu}" }, { "id": 9648, "title": "#{filter_genre_de}" }, { "id": 10749, "title": "#{filter_genre_md}" }, { "id": 878, "title": "#{filter_genre_fa}" }, { "id": 10770, "title": "#{filter_genre_tv}" }, { "id": 53, "title": "#{filter_genre_tr}" }, { "id": 10752, "title": "#{filter_genre_mi}" }, { "id": 37, "title": "#{filter_genre_ve}" }], tv: [{ "id": 10759, "title": "#{filter_genre_aa}" }, { "id": 16, "title": "#{filter_genre_mv}" }, { "id": 35, "title": "#{filter_genre_cm}" }, { "id": 80, "title": "#{filter_genre_cr}" }, { "id": 99, "title": "#{filter_genre_dc}" }, { "id": 18, "title": "#{filter_genre_dr}" }, { "id": 10751, "title": "#{filter_genre_fm}" }, { "id": 10762, "title": "#{filter_genre_ch}" }, { "id": 9648, "title": "#{filter_genre_de}" }, { "id": 10763, "title": "#{filter_genre_nw}" }, { "id": 10764, "title": "#{filter_genre_rs}" }, { "id": 10765, "title": "#{filter_genre_hf}" }, { "id": 10766, "title": "#{filter_genre_op}" }, { "id": 10767, "title": "#{filter_genre_tc}" }, { "id": 10768, "title": "#{filter_genre_mp}" }, { "id": 37, "title": "#{filter_genre_ve}" }] }; var keywords = { movie: [{ id: 9663, title: '#{filter_keyword_sequel}' }, { id: 9717, title: '#{filter_keyword_based_comic}' }, { id: 9882, title: '#{filter_keyword_space}' }, { id: 10349, title: '#{filter_keyword_survival}' }, { id: 12377, title: '#{filter_keyword_zombie}' }, { id: 10873, title: '#{filter_keyword_school}' }, { id: 9748, title: '#{filter_keyword_revenge}' }, { id: 703, title: '#{filter_keyword_detective}' }, { id: 14544, title: '#{filter_keyword_robots}' }, { id: 3133, title: '#{filter_keyword_vampires}' }, { id: 9951, title: '#{filter_keyword_aliens}' }, { id: 6149, title: '#{filter_keyword_police}' }, { id: 5484, title: '#{filter_keyword_reincarnation}' }, { id: 470, title: '#{filter_keyword_spy}' }, { id: 10617, title: '#{filter_keyword_disaster}' }], tv: [{ id: 9882, title: '#{filter_keyword_space}' }, { id: 10349, title: '#{filter_keyword_survival}' }, { id: 12377, title: '#{filter_keyword_zombie}' }, { id: 10873, title: '#{filter_keyword_school}' }, { id: 9748, title: '#{filter_keyword_revenge}' }, { id: 703, title: '#{filter_keyword_detective}' }, { id: 14544, title: '#{filter_keyword_robots}' }, { id: 3133, title: '#{filter_keyword_vampires}' }, { id: 9951, title: '#{filter_keyword_aliens}' }, { id: 6149, title: '#{filter_keyword_police}' }, { id: 5484, title: '#{filter_keyword_reincarnation}' }, { id: 470, title: '#{filter_keyword_spy}' }, { id: 10617, title: '#{filter_keyword_disaster}' }] }; function url$1(u) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var ln = [Storage.field('tmdb_lang')]; var genre = params.genres; if (permit.child_small) genre = genre ? genre + (genre == '16' ? '' : ',16') : '16'; if (params.langs) ln = typeof params.langs == 'string' ? [params.langs] : ln.concat(params.langs.filter(function (n) { return n !== ln[0]; })); u = add$a(u, 'api_key=' + TMDB$1.key()); u = add$a(u, 'language=' + ln.join(',')); if (genre && u.indexOf('with_genres') == -1) u = add$a(u, 'with_genres=' + genre); if (params.page) u = add$a(u, 'page=' + params.page); if (params.query) u = add$a(u, 'query=' + params.query); if (params.keywords) u = add$a(u, 'with_keywords=' + params.keywords); if (params.watch_region) u = add$a(u, 'watch_region=' + params.watch_region); if (params.watch_providers) u = add$a(u, 'with_watch_providers=' + params.watch_providers); if (params.companies) u = add$a(u, 'with_companies=' + params.companies); if (params.networks) u = add$a(u, 'with_networks=' + params.networks); if (params.sort_by) u = add$a(u, 'sort_by=' + params.sort_by); if (params.orig_lang) u = add$a(u, 'with_original_language=' + params.orig_lang); if (params.filter) { for (var i in params.filter) { u = add$a(u, i + '=' + params.filter[i]); } } // Добавляем проверку для запросов по жанрам if (params.genres && u.indexOf('discover/') !== 0) { u = 'discover/' + u; } return TMDB$1.api(u); } function add$a(u, params) { return u + (/\?/.test(u) ? '&' : '?') + params; } function get$c(method) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var oncomplite = arguments.length > 2 ? arguments[2] : undefined; var onerror = arguments.length > 3 ? arguments[3] : undefined; var cache = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; var u = url$1(method, params); var s = method.match(/tv\/(\d+)\/season\/(\d+)/); network$7.timeout(1000 * 10); network$7.silent(u, function (json) { json.url = method; json.source = source$2; // Исправляем сезоны для сериалов if (s && s[2] == 1) { var _seasons = Utils$1.splitEpisodesIntoSeasons(json.episodes); // Считаем количество реальных сезонов и отмечаем в объекте json.seasons_count = Arrays.getKeys(_seasons).length; // Если есть 1й сезон, то заменяем эпизоды на него if (_seasons[1]) { // Оставляем оригинальные эпизоды на случай, если понадобятся все эпизоды json.episodes_original = json.episodes; // Заменяем данные на 1й сезон json.episodes = _seasons[1]; } } // Фильтруем результаты по ключевым словам, чтобы не показывать фильмы с неуместными словами в названии if (json.results && Arrays.isArray(json.results)) { json.results = json.results.filter(function (item) { var title = (item.title || item.name || '').toLowerCase(); return !Keys.filter.find(function (word) { var reg = new RegExp(word, 'i'); return reg.test(title); }); }); } oncomplite(Utils$1.addSource(json, source$2)); }, function () { // Если сезон не найден, то пробуем найти правильный сезон из 1го сезона if (s) seasonFix(parseInt(s[2]), method, params = {}, oncomplite, onerror, cache);else if (onerror) onerror(); }, false, { cache: cache }); } function img$1(src, size) { var poster_size = Storage.field('poster_size'); var baseimg = 't/p/' + poster_size + '/'; var path = baseimg; if (size) path = path.replace(new RegExp(poster_size, 'g'), size); return src ? TMDB$1.image(path + src) : ''; } function seasonFix(season_need, method) { var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var oncomplite = arguments.length > 3 ? arguments[3] : undefined; var onerror = arguments.length > 4 ? arguments[4] : undefined; var cache = arguments.length > 5 ? arguments[5] : undefined; method = method.replace(/\/season\/(\d+)/, '/season/1'); network$7.timeout(1000 * 10); network$7.silent(url$1(method, params), function (new_json) { new_json.url = method; new_json.source = source$2; var seasons = Utils$1.splitEpisodesIntoSeasons(new_json.episodes); // Считаем количество реальных сезонов и отмечаем в объекте new_json.seasons_count = Arrays.getKeys(seasons).length; // Если нужный сезон есть, то отдаем его if (seasons[season_need]) { // Заменяем данные на нужный сезон new_json.episodes = seasons[season_need]; new_json.season_number = season_need; new_json.name = Lang.translate('torrent_serial_season') + ' ' + season_need; oncomplite(Utils$1.addSource(new_json, source$2)); } else if (onerror) onerror(); }, onerror, false, { cache: cache }); } function find$2(find) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var finded; var filtred = function filtred(items) { for (var i = 0; i < items.length; i++) { var item = items[i]; if (params.original_title == item.original_title || params.title == item.title) { finded = item; break; } } }; if (find.movie && find.movie.results.length) filtred(find.movie.results); if (find.tv && find.tv.results.length && !finded) filtred(find.tv.results); return finded; } function main$2() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var parts_limit = 6; var parts_data = [function (call) { get$c('movie/now_playing', params, function (json) { json.title = Lang.translate('title_now_watch'); call(json); }, call, { life: day$1 * 2 }); }, function (call) { get$c('trending/movie/day', params, function (json) { json.title = Lang.translate('title_trend_day'); call(json); }, call, { life: day$1 * 2 }); }, function (call) { get$c('trending/movie/week', params, function (json) { json.title = Lang.translate('title_trend_week'); call(json); }, call, { life: day$1 * 3 }); }, function (call) { get$c('movie/upcoming', params, function (json) { json.title = Lang.translate('title_upcoming'); call(json); }, call, { life: day$1 * 7 }); }, function (call) { get$c('movie/popular', params, function (json) { json.title = Lang.translate('title_popular_movie'); json.icon_svg = Template.string('icon_fire'); json.icon_bgcolor = '#fff'; json.icon_color = '#fd4518'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day$1 * 3 }); }, function (call) { get$c('trending/tv/week', params, function (json) { json.title = Lang.translate('title_popular_tv'); json.icon_svg = Template.string('icon_fire'); json.icon_bgcolor = '#fff'; json.icon_color = '#fd4518'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day$1 * 3 }); }, function (call) { get$c('movie/top_rated', params, function (json) { json.title = Lang.translate('title_top_movie'); json.icon_svg = Template.string('icon_top'); json.icon_bgcolor = '#e02129'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day$1 * 7 }); }, function (call) { get$c('tv/top_rated', params, function (json) { json.title = Lang.translate('title_top_tv'); json.icon_svg = Template.string('icon_top'); json.icon_bgcolor = '#e02129'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day$1 * 7 }); }]; ContentRows.call('main', params, parts_data); var start_shuffle = parts_data.length + 2; Arrays.insert(parts_data, 0, Api.partKeywords(parts_data, 'movie', start_shuffle)); Arrays.insert(parts_data, 0, Api.partPersons(parts_data, parts_limit, 'movie', start_shuffle)); genres$1.movie.forEach(function (genre) { var event = function event(call) { get$c('discover/movie?with_genres=' + genre.id, params, function (json) { json.title = Lang.translate(genre.title.replace(/[^a-z_]/g, '')); call(json); }, call, { life: day$1 * 7 }); }; parts_data.push(event); }); function loadPart(partLoaded, partEmpty) { Api.partNext(parts_data, parts_limit, partLoaded, partEmpty); } loadPart(oncomplite, onerror); return loadPart; } function category$3() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var fullcat = !(params.genres || params.keywords); var years = [2000, 2010, 2015]; var parts_limit = 6; var parts_data = [function (call) { if (params.url == 'movie') { get$c('discover/' + params.url + '?with_release_type=1', params, function (json) { json.title = Lang.translate('title_now_watch'); call(json); }, call, { life: day$1 * 2 }); } else call(); }, function (call) { get$c(params.url == 'movie' ? 'trending/movie/week' : 'trending/tv/week', params, function (json) { json.title = Lang.translate('title_popular'); json.icon_svg = Template.string('icon_fire'); json.icon_bgcolor = '#fff'; json.icon_color = '#fd4518'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day$1 * 3 }); }, function (call) { get$c('discover/' + params.url + '?' + (params.url == 'movie' ? 'primary_release_year' : 'first_air_date_year') + '=' + (new Date().getFullYear() - 1), params, function (json) { json.title = Lang.translate('title_last_year'); call(json); }, call, { life: day$1 * 7 }); }, function (call) { var lte = new Date().getFullYear() - 2 + '-12-31'; var gte = new Date().getFullYear() - 7 + '-01-01'; var reg = params.url == 'movie' ? 'primary_release_date' : 'first_air_date'; lte = reg + '.lte=' + lte; gte = reg + '.gte=' + gte; get$c('discover/' + params.url + '?' + lte + '&' + gte, params, function (json) { json.title = Lang.translate('title_worth_rewatch'); call(json); }, call, { life: day$1 * 7 }); }, function (call) { var lte = new Date().getFullYear() - 2 + '-12-31'; var gte = new Date().getFullYear() - 7 + '-01-01'; var reg = params.url == 'movie' ? 'primary_release_date' : 'first_air_date'; lte = reg + '.lte=' + lte; gte = reg + '.gte=' + gte; get$c('discover/' + params.url + '?' + lte + '&' + gte + '&vote_average.gte=8&vote_average.lte=9', params, function (json) { json.title = Lang.translate('title_hight_voite'); json.icon_svg = Template.string('icon_star'); json.icon_bgcolor = '#fff'; json.icon_color = '#212121'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day$1 * 7 }); }, function (call) { if (params.genres) return call(); if (params.url == 'tv') { get$c('trending/tv/week', params, function (json) { json.title = Lang.translate('title_this_week'); call(json); }, call, { life: day$1 * 3 }); } else { get$c('movie/upcoming', params, function (json) { json.title = Lang.translate('title_upcoming'); json.results.forEach(function (card) { card.params = { style: { name: 'wide' } }; }); json.params = { items: { view: 3 } }; call(json); }, call, { life: day$1 * 7 }); } }]; // Дух Рождества //Arrays.insert(parts_data, 1, Api.partKeyword({id: 207317, title: '#{filter_keyword_christmas}'}, params.url, params)) ContentRows.call('category', params, parts_data); var start_shuffle = parts_data.length + 2; Arrays.insert(parts_data, 0, Api.partKeywords(parts_data, params.url, start_shuffle, [], params)); if (fullcat) Arrays.insert(parts_data, 0, Api.partPersons(parts_data, parts_limit, params.url, start_shuffle)); if (fullcat) { genres$1[params.url].forEach(function (genre) { var event = function event(call) { get$c('discover/' + params.url + '?with_genres=' + genre.id, params, function (json) { json.title = Lang.translate(genre.title.replace(/[^a-z_]/g, '')); call(json); }, call, { life: day$1 * 7 }); }; parts_data.push(event); }); } years.forEach(function (year) { var lte = year + 5 + '-12-31'; var gte = year + '-01-01'; var reg = params.url == 'movie' ? 'primary_release_date' : 'first_air_date'; lte = reg + '.lte=' + lte; gte = reg + '.gte=' + gte; var eventYear = function eventYear(call) { get$c('discover/' + params.url + '?' + gte + '&' + lte, params, function (json) { json.title = Lang.translate('title_best_of_' + year); call(json); }, call, { life: day$1 * 7 }); }; parts_data.push(eventYear); var eventComedy = function eventComedy(call) { get$c('discover/' + params.url + '?' + gte + '&' + lte + '&with_genres=35', params, function (json) { json.title = Lang.translate('title_comedy_of_' + year); call(json); }, call, { life: day$1 * 7 }); }; if (fullcat) parts_data.push(eventComedy); }); Arrays.shuffleArrayFromIndex(parts_data, start_shuffle); function loadPart(partLoaded, partEmpty) { Api.partNext(parts_data, parts_limit, partLoaded, partEmpty); } loadPart(oncomplite, onerror); return loadPart; } function full$3() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var status = new Status(9); status.onComplite = oncomplite; var image_language = (Storage.field('tmdb_lang') || 'en').split(/[-_]/)[0]; var image_languages = [image_language, 'en', 'null'].filter(function (language, index, list) { return list.indexOf(language) == index; }).join(','); if (Utils$1.dcma(params.method, params.id)) return onerror(); get$c(params.method + '/' + params.id + '?append_to_response=content_ratings,release_dates,external_ids,keywords,alternative_titles,images&include_image_language=' + image_languages, params, function (json) { var _json$overview; json.source = 'tmdb'; if (json.external_ids) { json.imdb_id = json.external_ids.imdb_id; } if (!((_json$overview = json.overview) !== null && _json$overview !== void 0 && _json$overview.trim()) && Storage.field('tmdb_lang') !== 'en') { get$c(params.method + '/' + params.id, { langs: 'en' }, function (enjson) { var _enjson$overview; if ((_enjson$overview = enjson.overview) !== null && _enjson$overview !== void 0 && _enjson$overview.trim()) json.overview = enjson.overview; status.need--; status.check(); }, status.error.bind(status), { life: day$1 * 7 }); } else status.need--; if (params.method == 'tv') { var season = Utils$1.countSeasons(json); get$c('tv/' + json.id + '/season/' + season, {}, function (ep) { status.append('episodes', ep); }, status.error.bind(status), { life: day$1 * 3 }); } else status.need--; if (json.belongs_to_collection) { get$c('collection/' + json.belongs_to_collection.id, {}, function (collection) { collection.results = collection.parts.slice(0, 19); status.append('collection', collection); }, status.error.bind(status), { life: day$1 * 7 }); } else status.need--; status.append('movie', json); }, function () { status.need -= 2; status.error(); }, { life: day$1 * 7 }); if (!permit.child_small) { get$c(params.method + '/' + params.id + '/credits', params, function (json) { status.append('persons', json); }, status.error.bind(status), { life: day$1 * 7 }); get$c(params.method + '/' + params.id + '/recommendations', params, function (json) { status.append('recomend', json); }, status.error.bind(status), { life: day$1 * 7 }); get$c(params.method + '/' + params.id + '/similar', params, function (json) { status.append('simular', json); }, status.error.bind(status), { life: day$1 * 7 }); if (!permit.child) { videos(params, function (json) { status.append('videos', json); }, status.error.bind(status)); } else { status.need--; } } else { status.need -= 4; } Api.sources.cub.reactionsGet(params, function (json) { status.append('reactions', json); }); if (Lang.selected(['ru', 'uk', 'be']) && window.lampa_settings.account_use && !permit.child) { status.need++; Api.sources.cub.discussGet(params, function (json) { status.append('discuss', json); }, status.error.bind(status)); } if (params.method == 'movie') { status.need++; Api.sources.cub.metadataGet(params, function (json) { status.append('metadata', json); }, status.error.bind(status)); } } function videos() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var lg = Storage.field('tmdb_lang'); var status = new Status(lg == 'en' ? 1 : 2); status.onComplite = function (res) { var data = []; if (res.one && res.one.results && res.one.results.length) data = data.concat(res.one.results); if (res.two && res.two.results && res.two.results.length) data = data.concat(res.two.results); oncomplite({ results: data }); }; get$c(params.method + '/' + params.id + '/videos', { langs: Storage.field('tmdb_lang') }, function (json) { status.append('one', json); }, status.error.bind(status), { life: day$1 * 7 }); if (lg !== 'en') { get$c(params.method + '/' + params.id + '/videos', { langs: 'en' }, function (json) { status.append('two', json); }, status.error.bind(status), { life: day$1 * 7 }); } } function list$5() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var u = url$1(params.url, params); network$7.silent(u, function (data) { oncomplite(Utils$1.addSource(data, source$2)); }, onerror, false, { cache: { life: day$1 * 2 } }); } function search$5() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var status = new Status(3); status.onComplite = function (data) { var items = []; if (data.movie && data.movie.results.length) items.push(data.movie); if (data.tv && data.tv.results.length) items.push(data.tv); if (data.person && data.person.results.length) items.push(data.person); oncomplite(items); }; get$c('search/movie', params, function (json) { json.title = Lang.translate('menu_movies'); json.type = 'movie'; status.append('movie', json); }, status.error.bind(status)); get$c('search/tv', params, function (json) { json.title = Lang.translate('menu_tv'); json.type = 'tv'; status.append('tv', json); }, status.error.bind(status)); get$c('search/person', params, function (json) { json.title = Lang.translate('title_actors'); json.type = 'person'; json.results.forEach(function (person) { person.params = { module: Helper$9.only('Card', 'Release', 'Callback') }; }); status.append('person', json); }, status.error.bind(status)); } function discovery$2() { return { title: 'TMDB', search: search$5, params: { save: true }, onRecall: function onRecall(rows, last_query) { rows.forEach(function (row) { if (row.type == 'person') { row.results.forEach(function (element) { element.params = { module: Helper$9.only('Card', 'Release', 'Callback') }; }); } }); }, onSelect: function onSelect(params, close) { close(); router.call(params.element.gender ? 'actor' : 'full', params.element); }, onMore: function onMore(params, close) { close(); Activity.push({ url: 'search/' + params.data.type, title: Lang.translate('search') + ' - ' + params.query, component: 'category_full', page: 2, query: encodeURIComponent(params.query), source: source$2 }); }, onCancel: network$7.clear.bind(network$7) }; } function person$2() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var sortCredits = function sortCredits(credits) { return Utils$1.addSource(credits.map(function (a) { a.year = parseInt(((a.release_date || a.first_air_date || '0000') + '').slice(0, 4)); return a; }).sort(function (a, b) { return b.vote_average - a.vote_average && b.vote_count - a.vote_count; }) //сортируем по оценке и кол-ву голосов (чтобы отсечь мусор с 1-2 оценками) , source$2); }; var convert = function convert(credits, person_data) { if (params.only_credits) { var cast = sortCredits(credits.cast); var result = {}; result[params.only_credits] = sortCredits(cast.filter(function (media) { return media.media_type === params.only_credits; })); return result; } else { var department = ['directing', 'writing', 'editing', 'creator']; credits.crew.forEach(function (a) { a.department = Lang.translate(department.indexOf(a.department.toLowerCase()) == -1 ? 'settings_main_rest' : 'full_' + a.department.toLowerCase()); }); var _cast = sortCredits(credits.cast), crew = sortCredits(credits.crew), tv = sortCredits(_cast.filter(function (media) { return media.media_type === 'tv'; })), movie = sortCredits(_cast.filter(function (media) { return media.media_type === 'movie'; })), knownFor; //Наиболее известные работы человека //1. Группируем все работы по департаментам (Актер, Режиссер, Сценарист и т.д.) knownFor = Arrays.groupBy(crew, 'department'); if (movie.length > 0) knownFor[Lang.translate('menu_movies')] = movie; if (tv.length > 0) knownFor[Lang.translate('menu_tv')] = tv; //2. Для каждого департамента суммируем кол-ва голосов (вроде бы сам TMDB таким образом определяет knownFor для людей) knownFor = Object.entries(knownFor).map(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), depIdx = _ref2[0], dep = _ref2[1]; //убираем дубликаты (человек может быть указан в одном департаменте несколько раз на разных должностях (job)) var set = {}, credits = dep.filter(function (credit) { return set.hasOwnProperty(credit.original_title || credit.original_name) ? false : credit.original_title ? set[credit.original_title] = true : set[credit.original_name] = true; }); return { name: depIdx, credits: credits, vote_count: dep.reduce(function (a, b) { return a + b.vote_count; }, 0) }; //3. Сортируем департаменты по кол-ву голосов }).sort(function (a, b) { return b.credits.length - a.credits.length; }); return { raw: credits, cast: _cast, crew: crew, tv: tv, movie: movie, knownFor: knownFor }; } }; var status = new Status(params.only_credits ? 1 : 2); status.onComplite = function () { var fulldata = {}; if (status.data.person) fulldata.person = status.data.person; if (status.data.credits) fulldata.credits = convert(status.data.credits, status.data.person || {}); oncomplite(fulldata); }; if (!params.only_credits) { get$c('person/' + params.id, params, function (json) { status.append('person', json); }, status.error.bind(status), { life: day$1 * 7 }); } get$c('person/' + params.id + '/combined_credits', params, function (json) { status.append('credits', json); }, status.error.bind(status), { life: day$1 * 7 }); } function menu$3() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; if (menu_list.length) oncomplite(menu_list);else { var u = url$1('genre/movie/list', params); network$7.silent(u, function (j) { j.genres.forEach(function (g) { menu_list.push({ title: g.name, id: g.id }); }); oncomplite(menu_list); }); } } function menuCategory$2(params, oncomplite) { var menu = []; if (params.action !== 'tv') { menu.push({ title: Lang.translate('title_now_watch'), url: params.action + '/now_playing' }); } menu.push({ title: Lang.translate('title_popular'), url: params.action + '/popular' }); var date = new Date(); var query = []; query.push('sort_by=release_date.desc'); query.push('year=' + date.getFullYear()); query.push('first_air_date_year=' + date.getFullYear()); query.push('vote_average.gte=7'); menu.push({ title: Lang.translate('title_new'), url: 'discover/' + params.action + '?' + query.join('&') }); if (params.action == 'tv') { menu.push({ title: Lang.translate('title_tv_today'), url: params.action + '/airing_today' }); menu.push({ title: Lang.translate('title_this_week'), url: params.action + '/on_the_air' }); } menu.push({ title: Lang.translate('title_in_top'), url: params.action + '/top_rated' }); oncomplite(menu); } function external_ids() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; get$c((params.type || 'tv') + '/' + params.id + '/external_ids', params, oncomplite, onerror, { life: day$1 * 7 }); } function external_imdb_id() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; get$c(params.type + '/' + params.id + '/external_ids', params, function (ids) { oncomplite(ids.imdb_id || ''); }, function () { oncomplite(''); }, { life: day$1 * 7 }); } function company$1() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var status = new Status(3); status.onComplite = function () { function sortResultsByVoteAverage(results) { return results.sort(function (a, b) { return b.vote_average - a.vote_average; }); } if (status.data.company) { var fulldata = { company: status.data.company, lines: [] }; if (status.data.movie && status.data.movie.results.length) fulldata.lines.push({ total_pages: status.data.movie.total_pages, url: 'discover/movie', title: Lang.translate('menu_movies') + ' (' + status.data.movie.total_results + ')', results: sortResultsByVoteAverage(status.data.movie.results), total_results: status.data.movie.total_results }); if (status.data.tv && status.data.tv.results.length) fulldata.lines.push({ total_pages: status.data.tv.total_pages, url: 'discover/tv', title: Lang.translate('menu_tv') + ' (' + status.data.tv.total_results + ')', results: sortResultsByVoteAverage(status.data.tv.results), total_results: status.data.tv.total_results }); oncomplite(fulldata); } else onerror(); }; get$c('company/' + params.id, params, function (json) { status.append('company', json); }, status.error.bind(status), { life: day$1 * 7 }); get$c('discover/movie?sort_by=vote_count.desc&with_companies=' + params.id, params, function (json) { status.append('movie', json); }, status.error.bind(status), { life: day$1 * 7 }); get$c('discover/tv?sort_by=vote_count.desc&with_companies=' + params.id, params, function (json) { status.append('tv', json); }, status.error.bind(status), { life: day$1 * 7 }); } function seasons$2(tv, from, oncomplite) { var status = new Status(from.length); status.onComplite = oncomplite; from.forEach(function (season) { get$c('tv/' + tv.id + '/season/' + season, {}, function (json) { status.append('' + season, json); }, status.error.bind(status), { life: day$1 * 3 }); }); } function parsePG(movie) { var pg; var cd = Storage.field('language'); if (movie.content_ratings) { try { var _find = movie.content_ratings.results.find(function (a) { return a.iso_3166_1 == cd.toUpperCase(); }); if (!_find) _find = movie.content_ratings.results.find(function (a) { return a.iso_3166_1 == 'US'; }); if (_find) pg = Utils$1.decodePG(_find.rating); } catch (e) {} } if (movie.release_dates && movie.release_dates.results && !pg) { try { var _find2 = movie.release_dates.results.find(function (a) { return a.iso_3166_1 == cd.toUpperCase(); }); if (!_find2) _find2 = movie.release_dates.results.find(function (a) { return a.iso_3166_1 == 'US'; }); if (_find2 && _find2.release_dates.length) { pg = Utils$1.decodePG(_find2.release_dates[0].certification); } } catch (e) {} } if (movie.restrict) pg = movie.restrict + '+'; return pg; } function parseCountries(movie) { var iso_countries = { 'AD': Lang.translate('country_ad'), 'AE': Lang.translate('country_ae'), 'AF': Lang.translate('country_af'), //'AG' : 'Antigua And Barbuda', //'AI' : 'Anguilla', 'AL': Lang.translate('country_al'), 'AM': Lang.translate('country_am'), //'AN' : 'Netherlands Antilles', 'AO': Lang.translate('country_ao'), //'AQ' : 'Antarctica', 'AR': Lang.translate('country_ar'), //'AS' : 'American Samoa', 'AT': Lang.translate('country_at'), 'AU': Lang.translate('country_au'), 'AW': Lang.translate('country_aw'), 'AZ': Lang.translate('country_az'), 'BA': Lang.translate('country_ba'), //'BB' : 'Barbados', 'BD': Lang.translate('country_bd'), 'BE': Lang.translate('country_be'), //'BF' : 'Burkina Faso', 'BG': Lang.translate('country_bg'), 'BH': Lang.translate('country_bh'), 'BI': Lang.translate('country_bi'), 'BJ': Lang.translate('country_bj'), //'BL' : 'Saint Barthelemy', //'BM' : 'Bermuda', //'BN' : 'Brunei Darussalam', 'BO': Lang.translate('country_bo'), 'BR': Lang.translate('country_br'), 'BS': Lang.translate('country_bs'), 'BT': Lang.translate('country_bt'), //'BV' : 'Bouvet Island', 'BW': Lang.translate('country_bw'), 'BY': Lang.translate('country_by'), //'BZ' : 'Belize', 'CA': Lang.translate('country_ca'), //'CC' : 'Cocos (Keeling) Islands', //'CD' : 'Congo, Democratic Republic', //'CF' : 'Central African Republic', //'CG' : 'Congo', 'CH': Lang.translate('country_ch'), //'CI' : 'Cote D\'Ivoire', //'CK' : 'Cook Islands', 'CL': Lang.translate('country_cl'), 'CM': Lang.translate('country_cm'), 'CN': Lang.translate('country_cn'), 'CO': Lang.translate('country_co'), 'CR': Lang.translate('country_cr'), 'CU': Lang.translate('country_cu'), 'CV': Lang.translate('country_cv'), //'CX' : 'Christmas Island', 'CY': Lang.translate('country_cy'), 'CZ': Lang.translate('country_cz'), 'DE': Lang.translate('country_de'), 'DJ': Lang.translate('country_dj'), 'DK': Lang.translate('country_dk'), //'DM' : 'Dominica', 'DO': Lang.translate('country_do'), 'DZ': Lang.translate('country_dz'), 'EC': Lang.translate('country_ec'), 'EE': Lang.translate('country_ee'), 'EG': Lang.translate('country_eg'), //'EH' : 'Western Sahara', //'ER' : 'Eritrea', 'ES': Lang.translate('country_es'), 'ET': Lang.translate('country_et'), 'FI': Lang.translate('country_fi'), //'FJ' : 'Fiji', //'FK' : 'Falkland Islands (Malvinas)', //'FM' : 'Micronesia, Federated States Of', 'FO': Lang.translate('country_fo'), 'FR': Lang.translate('country_fr'), 'GA': Lang.translate('country_ga'), 'GB': Lang.translate('country_gb'), //'GD' : 'Grenada', 'GE': Lang.translate('country_ge'), //'GF' : 'French Guiana', //'GG' : 'Guernsey', 'GH': Lang.translate('country_gh'), //'GI' : 'Gibraltar', 'GL': Lang.translate('country_gl'), //'GM' : 'Gambia', //'GN' : 'Guinea', 'GP': Lang.translate('country_gp'), //'GQ' : 'Equatorial Guinea', 'GR': Lang.translate('country_gr'), //'GS' : 'South Georgia And Sandwich Isl.', 'GT': Lang.translate('country_gt'), //'GU' : 'Guam', //'GW' : 'Guinea-Bissau', //'GY' : 'Guyana', 'HK': Lang.translate('country_hk'), //'HM' : 'Heard Island & Mcdonald Islands', //'HN' : 'Honduras', 'HR': Lang.translate('country_hr'), 'HT': Lang.translate('country_ht'), 'HU': Lang.translate('country_hu'), 'ID': Lang.translate('country_id'), 'IE': Lang.translate('country_ie'), 'IL': Lang.translate('country_il'), //'IM' : 'Isle Of Man', 'IN': Lang.translate('country_in'), //'IO' : 'British Indian Ocean Territory', 'IQ': Lang.translate('country_iq'), 'IR': Lang.translate('country_ir'), 'IS': Lang.translate('country_is'), 'IT': Lang.translate('country_it'), //'JE' : 'Jersey', 'JM': Lang.translate('country_jm'), 'JO': Lang.translate('country_jo'), 'JP': Lang.translate('country_jp'), 'KE': Lang.translate('country_ke'), 'KG': Lang.translate('country_kg'), 'KH': Lang.translate('country_kh'), //'KI' : 'Kiribati', //'KM' : 'Comoros', //'KN' : 'Saint Kitts And Nevis', 'KP': Lang.translate('country_kp'), 'KR': Lang.translate('country_kr'), 'KW': Lang.translate('country_kw'), //'KY' : 'Cayman Islands', 'KZ': Lang.translate('country_kz'), 'LA': Lang.translate('country_la'), 'LB': Lang.translate('country_lb'), //'LC' : 'Saint Lucia', 'LI': Lang.translate('country_li'), 'LK': Lang.translate('country_lk'), 'LR': Lang.translate('country_lr'), //'LS' : 'Lesotho', 'LT': Lang.translate('country_lt'), 'LU': Lang.translate('country_lu'), 'LV': Lang.translate('country_lv'), 'LY': Lang.translate('country_ly'), 'MA': Lang.translate('country_ma'), 'MC': Lang.translate('country_mc'), 'MD': Lang.translate('country_md'), 'ME': Lang.translate('country_me'), //'MF' : 'Saint Martin', //'MG' : 'Madagascar', //'MH' : 'Marshall Islands', 'MK': Lang.translate('country_mk'), //'ML' : 'Mali', 'MM': Lang.translate('country_mm'), 'MN': Lang.translate('country_mn'), 'MO': Lang.translate('country_mo'), //'MP' : 'Northern Mariana Islands', //'MQ' : 'Martinique', //'MR' : 'Mauritania', //'MS' : 'Montserrat', 'MT': Lang.translate('country_mt'), 'MU': Lang.translate('country_mu'), 'MV': Lang.translate('country_mv'), 'MW': Lang.translate('country_mw'), 'MX': Lang.translate('country_mx'), 'MY': Lang.translate('country_my'), 'MZ': Lang.translate('country_mz'), 'NA': Lang.translate('country_na'), //'NC' : 'New Caledonia', 'NE': Lang.translate('country_ne'), //'NF' : 'Norfolk Island', 'NG': Lang.translate('country_ng'), 'NI': Lang.translate('country_ni'), 'NL': Lang.translate('country_nl'), 'NO': Lang.translate('country_no'), 'NP': Lang.translate('country_np'), //'NR' : 'Nauru', //'NU' : 'Niue', 'NZ': Lang.translate('country_nz'), 'OM': Lang.translate('country_om'), 'PA': Lang.translate('country_pa'), 'PE': Lang.translate('country_pe'), //'PF' : 'French Polynesia', 'PG': Lang.translate('country_pg'), 'PH': Lang.translate('country_ph'), 'PK': Lang.translate('country_pk'), 'PL': Lang.translate('country_pl'), //'PM' : 'Saint Pierre And Miquelon', //'PN' : 'Pitcairn', 'PR': Lang.translate('country_pr'), 'PS': Lang.translate('country_ps'), 'PT': Lang.translate('country_pt'), //'PW' : 'Palau', 'PY': Lang.translate('country_py'), 'QA': Lang.translate('country_qa'), //'RE' : 'Reunion', 'RO': Lang.translate('country_ro'), 'RS': Lang.translate('country_rs'), 'RU': Lang.translate('country_ru'), 'RW': Lang.translate('country_rw'), 'SA': Lang.translate('country_sa'), //'SB' : 'Solomon Islands', //'SC' : 'Seychelles', 'SD': Lang.translate('country_sd'), 'SE': Lang.translate('country_se'), 'SG': Lang.translate('country_sg'), //'SH' : 'Saint Helena', 'SI': Lang.translate('country_si'), //'SJ' : 'Svalbard And Jan Mayen', 'SK': Lang.translate('country_sk'), //'SL' : 'Sierra Leone', //'SM' : 'San Marino', 'SN': Lang.translate('country_sn'), //'SO' : 'Somalia', //'SR' : 'Suriname', //'ST' : 'Sao Tome And Principe', 'SU': Lang.translate('country_su'), 'SV': Lang.translate('country_sv'), 'SY': Lang.translate('country_sy'), //'SZ' : 'Swaziland', //'TC' : 'Turks And Caicos Islands', //'TD' : 'Chad', //'TF' : 'French Southern Territories', //'TG' : 'Togo', 'TH': Lang.translate('country_th'), 'TJ': Lang.translate('country_tj'), //'TK' : 'Tokelau', //'TL' : 'Timor-Leste', 'TM': Lang.translate('country_tm'), 'TN': Lang.translate('country_tn'), //'TO' : 'Tonga', 'TR': Lang.translate('country_tr'), //'TT' : 'Trinidad And Tobago', //'TV' : 'Tuvalu', 'TW': Lang.translate('country_tw'), 'TZ': Lang.translate('country_tz'), 'UA': Lang.translate('country_ua'), 'UG': Lang.translate('country_ug'), //'UM' : 'United States Outlying Islands', 'US': Lang.translate('country_us'), 'UY': Lang.translate('country_uy'), 'UZ': Lang.translate('country_uz'), //'VA' : 'Holy See (Vatican City State)', //'VC' : 'Saint Vincent And Grenadines', 'VE': Lang.translate('country_ve'), //'VG' : 'Virgin Islands, British', //'VI' : 'Virgin Islands, U.S.', 'VN': Lang.translate('country_vn'), //'VU' : 'Vanuatu', //'WF' : 'Wallis And Futuna', 'WS': Lang.translate('country_ws'), 'YE': Lang.translate('country_ye'), //'YT' : 'Mayotte', 'YU': Lang.translate('country_yu'), 'ZA': Lang.translate('country_za'), 'ZM': Lang.translate('country_zm'), 'ZW': Lang.translate('country_zw') }; return movie.production_countries ? movie.production_countries.map(function (a) { //let cc = 'country_' + a.iso_3166_1.toLowerCase() //return Lang.translate(cc) // FIXME! return a.name as fallback if (iso_countries.hasOwnProperty(a.iso_3166_1)) { return iso_countries[a.iso_3166_1]; } else { return a.name; } }) : ''; } function getGenresNameFromIds(card_type, ids) { var finded = []; var where = genres$1[card_type]; ids.forEach(function (a) { var find = where.find(function (i) { return i.id == a; }); if (find) finded.push(Utils$1.capitalizeFirstLetter(Lang.translate(find.title))); }); return finded; } function clear$a() { network$7.clear(); } var TMDB = { main: main$2, menu: menu$3, img: img$1, full: full$3, list: list$5, category: category$3, search: search$5, clear: clear$a, company: company$1, person: person$2, seasons: seasons$2, find: find$2, external_ids: external_ids, get: get$c, menuCategory: menuCategory$2, discovery: discovery$2, parsePG: parsePG, parseCountries: parseCountries, genres: genres$1, keywords: keywords, external_imdb_id: external_imdb_id, getGenresNameFromIds: getGenresNameFromIds, videos: videos }; var Card$2 = { onInit: function onInit() { var _this = this; this.getPosterPath = function () { var src = ''; if (_this.params.style.name == 'wide' && _this.data.backdrop_path) src = TMDB.img(_this.data.backdrop_path, 'w780');else if (_this.params.style.name == 'collection' && _this.data.backdrop_path) src = TMDB.img(_this.data.backdrop_path, 'w500');else if (_this.data.poster_path) src = TMDB.img(_this.data.poster_path);else if (_this.data.profile_path) src = TMDB.img(_this.data.profile_path);else if (_this.data.poster) src = _this.data.poster;else if (_this.data.img) src = _this.data.img;else src = './img/img_broken.svg'; return src; }; }, onCreate: function onCreate() { var _this2 = this, _this$html$find; this.html = Template.js('card'); this.img = this.html.find('.card__img') || {}; this.img_errors = 0; this.html.addClass('card--loaded'); this.img.onerror = function () { var load_src = _this2.img.src; console.log('Img', 'noload', load_src); TMDB$1.broken(load_src); _this2.img_errors++; clearTimeout(_this2.img_timer); if (_this2.img_errors > 3) { _this2.img.src = './img/img_broken.svg'; $(_this2.img).after(Template.elem('div', { "class": 'card__img-broken', children: [Template.elem('div', { children: [Template.elem('div', { text: vpn.code().toUpperCase() }), Template.elem('br'), Template.elem('div', { text: load_src.split('?')[0] })] })] })); _this2.img.onerror = function () {}; } else { _this2.img_timer_next = setTimeout(function () { var next_src = _this2.getPosterPath(); if (next_src == load_src) _this2.img.onerror();else _this2.img.src = next_src; }, 2000); } }; this.html.card_data = this.data; (_this$html$find = this.html.find('.card__title')) === null || _this$html$find === void 0 ? void 0 : _this$html$find.text(this.data.title || ''); this.html.on('visible', this.emit.bind(this, 'visible')); }, onVisible: function onVisible() { var _this3 = this; var src = this.getPosterPath(); this.img_timer = setTimeout(function () { _this3.img.onerror(); }, 1000 * 15); this.img.onload = function () { clearTimeout(_this3.img_timer); clearTimeout(_this3.img_timer_next); }; this.img.src = src; this.emit('update'); }, onDestroy: function onDestroy() { this.img.onerror = function () {}; this.img.onload = function () {}; clearTimeout(this.img_timer); clearTimeout(this.img_timer_next); this.img.src = ''; } }; var Folder = { onCreate: function onCreate() { this.html = Template.js('bookmarks_folder'); this.html.find('.bookmarks-folder__title').text(Lang.translate('menu_' + this.data.media)); this.html.find('.bookmarks-folder__num').text(this.data.results.length); this.html.on('visible', this.emit.bind(this, 'visible')); }, onImage: function onImage(src, i) { var _this = this; var img = Template.elem('img', { "class": 'card__img i-' + i }); this.html.find('.bookmarks-folder__body').append(img); Utils$1.imgLoad(img, src, function () { _this.html.addClass('card--loaded'); }, function () { img.src = './img/img_broken.svg'; }); }, onVisible: function onVisible() { var _this2 = this; var filtred = this.data.results.filter(function (a) { return a.poster_path; }).slice(0, 3); filtred.forEach(function (a, i) { _this2.emit('image', TMDB.img(a.poster_path), i); }); if (filtred.length == 0) this.emit('image', './img/img_load.svg'); } }; var Icons = { onCreate: function onCreate() { var data = this.data; if (data.original_name) { var _this$html$find; (_this$html$find = this.html.find('.card__view')) === null || _this$html$find === void 0 ? void 0 : _this$html$find.append(Template.elem('div', { "class": 'card__type', text: data.original_name ? 'TV' : 'MOV' })); this.html.addClass(data.original_name ? 'card--tv' : 'card--movie'); } var qu = data.quality || data.release_quality; if (qu && Storage.field('card_quality') && !data.original_name) { var _this$html$find2; (_this$html$find2 = this.html.find('.card__view')) === null || _this$html$find2 === void 0 ? void 0 : _this$html$find2.append(Template.elem('div', { "class": 'card__quality', children: [Template.elem('div', { text: qu })] })); } } }; var Ratting = { onCreate: function onCreate() { var vote = parseFloat((this.data.cub_hundred_rating || this.data.vote_average || 0) + '').toFixed(1); if (vote > 0) { this.html.find('.card__view').append(Template.elem('div', { "class": 'card__vote', text: this.data.cub_hundred_fire ? Utils$1.bigNumberToShort(this.data.cub_hundred_fire) : vote >= 10 ? 10 : vote })); } } }; var Release = { onCreate: function onCreate() { var release_year = ((this.data.release_date || this.data.birthday || '0000') + '').slice(0, 4); if (release_year == '0000') { var _this$html$find; (_this$html$find = this.html.find('.card__age')) === null || _this$html$find === void 0 ? void 0 : _this$html$find.remove(); } else { var _this$html$find2; (_this$html$find2 = this.html.find('.card__age')) === null || _this$html$find2 === void 0 ? void 0 : _this$html$find2.text(release_year); } } }; var Favorite$1 = { onCreate: function onCreate() { var _this = this; var onCheck = function onCheck(a) { Favorite.toggle(a.where, _this.data); }; var onSelect = function onSelect(a) { onCheck(a); _this.emit('menuSelect', a, _this.html, _this.data); }; var onDraw = function onDraw(item) { if (!Account$1.hasPremium()) { var wrap = $('
    '); wrap.append(Template.js('icon_lock')); item.find('.selectbox-item__checkbox').remove(); item.append(wrap); item.on('hover:enter', function () { Select.close(); Account$1.showCubPremium(); }); } }; function drawMenu() { var status = Favorite.check(this.data); var menu = []; var items_check = ['book', 'like', 'wath', 'history']; var items_mark = ['look', 'viewed', 'scheduled', 'continued', 'thrown']; items_check.forEach(function (c) { menu.push({ title: Lang.translate('title_' + c), where: c, checkbox: true, checked: status[c], onCheck: onCheck }); }); if (window.lampa_settings.account_use) { menu.push({ title: Lang.translate('settings_cub_status'), separator: true }); items_mark.forEach(function (m) { menu.push({ title: Lang.translate('title_' + m), where: m, picked: Account$1.hasPremium() ? status[m] : false, collect: true, noenter: !Account$1.hasPremium(), onSelect: onSelect, onDraw: onDraw }); }); } return menu; } this.menu_list.push({ title: Lang.translate('settings_input_links'), menu: drawMenu.bind(this) }); this.listenerFavorite = function (e) { if (e.target == 'favorite') { if (e.card) { if (e.card.id == _this.data.id) _this.emit('favorite'); } else _this.emit('favorite'); } }; Lampa.Listener.follow('state:changed', this.listenerFavorite); }, onUpdate: function onUpdate() { this.emit('favorite'); }, onAddicon: function onAddicon(name) { this.html.find('.card__icons-inner').append(Template.elem('div', { "class": 'card__icon icon--' + name })); }, onFavorite: function onFavorite() { var status = Favorite.check(this.data); var marker = this.html.find('.card__marker'); var marks = ['look', 'viewed', 'scheduled', 'continued', 'thrown']; this.html.find('.card__icons-inner').innerHTML = ''; if (status.book) this.emit('addicon', 'book'); if (status.like) this.emit('addicon', 'like'); if (status.wath) this.emit('addicon', 'wath'); if (status.history || Timeline$1.watched(this.data)) this.emit('addicon', 'history'); var any_marker = marks.find(function (m) { return status[m]; }); if (any_marker) { if (!marker) { marker = Template.elem('div', { "class": 'card__marker', children: [Template.elem('span')] }); this.html.find('.card__view').append(marker); } marker.find('span').text(Lang.translate('title_' + any_marker)); marker.removeClass(marks.map(function (m) { return 'card__marker--' + m; }).join(' ')).addClass('card__marker--' + any_marker); } else if (marker) marker.remove(); }, onDestroy: function onDestroy() { Lampa.Listener.remove('state:changed', this.listenerFavorite); } }; var Callback$1 = { onCreate: function onCreate() { this.html.on('hover:focus', this.emit.bind(this, 'focus', this.html, this.data)); this.html.on('hover:touch', this.emit.bind(this, 'touch', this.html, this.data)); this.html.on('hover:hover', this.emit.bind(this, 'hover', this.html, this.data)); this.html.on('hover:enter', this.emit.bind(this, 'enter', this.html, this.data)); this.html.on('hover:long', this.emit.bind(this, 'long', this.html, this.data)); } }; var Line$6 = { onCreate: function onCreate() { var _this = this; this.html = Template.js('season_episode', this.data); this.prefix = Template.prefix(this.html, 'season-episode'); if (this.data.vote_average) { this.prefix.info.append(Template.js('season_episode_rate', { rate: parseFloat(this.data.vote_average + '').toFixed(1) })); } if (this.data.air_date) { this.data.vote_average && this.prefix.info.append(Template.elem('span', { "class": 'season-episode-split', text: '●' })); this.prefix.info.append(Template.elem('span', { text: Utils$1.parseTime(this.data.air_date).full })); } if (this.data.left_days) { this.prefix.quality.text(this.data.left_text); this.html.style.opacity = 0.5; } var visible = function visible() { _this.prefix.loader.remove(); _this.prefix.img.append(Template.elem('div', { "class": 'season-episode__episode-number', text: ('0' + _this.data.episode_number).slice(-2) })); }; this.prefix.timeline.append(Timeline$1.render(this.data.timeline)); this.html.on('visible', function () { var img = _this.html.find('img'); img.onerror = function () { img.src = './img/img_broken.svg'; }; img.onload = function () { _this.prefix.img.addClass('season-episode__img--loaded'); visible(); }; if (_this.data.still_path) img.src = TMDB.img('t/p/w300' + _this.data.still_path);else if (_this.data.img) img.src = _this.data.img;else visible(); }); }, onViewed: function onViewed() { var _this$html$find; (_this$html$find = this.html.find('.season-episode__viewed')) === null || _this$html$find === void 0 ? void 0 : _this$html$find.remove(); var mark = Template.elem('div', { "class": 'season-episode__viewed', children: [Template.js('icon_viewed')] }); if (Boolean(this.data.timeline.percent)) this.html.find('.season-episode__img').append(mark); } }; var Small = { onCreate: function onCreate() { var _this = this; this.html = Template.js('full_episode', this.data); this.prefix = Template.prefix(this.html, 'full-episode'); this.html.addClass('full-episode--small'); this.html.append(Template.elem('div', { "class": 'full-episode__viewed', children: [Template.js('icon_viewed')] })); this.html.on('visible', function () { var src = './img/img_broken.svg'; if (_this.data.still_path) src = TMDB.img(_this.data.still_path, 'w300');else if (_this.data.img) src = _this.data.img; Utils$1.imgLoad(_this.html.find('img'), src, function () { _this.html.addClass('full-episode--loaded'); }); }); }, onViewed: function onViewed() { this.html.toggleClass('full-episode--viewed', Boolean(this.data.timeline.percent)); } }; var SmallNext = { onCreate: function onCreate() { this.html = Template.js('full_episode', this.data); this.html.addClass('full-episode--small full-episode--next'); } }; var Mark = { onCreate: function onCreate() { var _this = this; this.html.on('hover:enter', function () { if (Boolean(_this.data.timeline.percent)) { _this.data.timeline.time = 0; _this.data.timeline.percent = 0; } else { _this.data.timeline.time = typeof _this.data.timeline.duration == 'number' ? _this.data.timeline.duration * 0.95 : 0; _this.data.timeline.percent = 95; } Timeline$1.update(_this.data.timeline); _this.emit('viewed'); }); this.emit('viewed'); this.listenerTimeline = function (e) { if (e.target == 'timeline' && e.reason == 'update' && e.data.hash == _this.data.timeline.hash) { for (var i in e.data.road) { _this.data.timeline[i] = e.data.road[i]; } _this.emit('viewed'); } }; Lampa.Listener.follow('state:changed', this.listenerTimeline); }, onDestroy: function onDestroy() { Lampa.Listener.remove('state:changed', this.listenerTimeline); } }; var Card$1 = { onInit: function onInit() { this.card = this.data.card || {}; }, onCreate: function onCreate() { var _this = this; this.html = Template.js('card_episode'); this.img_poster = this.html.find('.card__img') || {}; this.img_episode = this.html.find('.full-episode__img img') || {}; this.img_poster.onerror = function () { _this.img_poster.src = './img/img_broken.svg'; }; this.img_episode.onload = function () { _this.html.find('.full-episode__img').addClass('full-episode__img--loaded'); }; this.img_episode.onerror = function () { _this.img_episode.src = './img/img_broken.svg'; }; this.html.find('.card__title').text(this.card.title || this.card.name); this.html.find('.full-episode__name').text(this.data.name || Lang.translate('noname')); this.html.find('.full-episode__num').text(this.data.episode_number || ''); this.html.find('.full-episode__date').text(this.data.air_date ? Utils$1.parseTime(this.data.air_date).full : '----'); var release_year = ((this.card.release_date || this.card.first_air_date || '0000') + '').slice(0, 4); if (release_year == '0000') { var _this$html$find; (_this$html$find = this.html.find('.card__age')) === null || _this$html$find === void 0 ? void 0 : _this$html$find.remove(); } else { this.html.find('.card__age').text(release_year); } this.html.on('visible', this.emit.bind(this, 'visible')); }, onVisible: function onVisible() { if (this.card.poster_path) this.img_poster.src = TMDB.img(this.card.poster_path);else if (this.card.profile_path) this.img_poster.src = TMDB.img(this.card.profile_path);else if (this.card.poster) this.img_poster.src = this.card.poster;else if (this.card.img) this.img_poster.src = this.card.img;else this.img_poster.src = './img/img_broken.svg'; if (this.data.still_path) this.img_episode.src = TMDB.img(this.data.still_path, 'w300');else if (this.card.backdrop_path) this.img_episode.src = TMDB.img(this.card.backdrop_path, 'w300');else if (this.data.img) this.img_episode.src = this.data.img;else this.img_episode.src = './img/img_broken.svg'; }, onDestroy: function onDestroy() { this.img_poster.onerror = function () {}; this.img_poster.onload = function () {}; this.img_episode.onerror = function () {}; this.img_episode.onload = function () {}; this.img_poster.src = ''; this.img_episode.src = ''; } }; var EpisodeMap = { Line: Line$6, Card: Card$1, Small: Small, SmallNext: SmallNext, Mark: Mark, Callback: Callback$1 }; var Episode = /*#__PURE__*/function (_Constructor) { _inherits(Episode, _Constructor); var _super = _createSuper(Episode); function Episode(data) { var _this; _classCallCheck(this, Episode); _this = _super.call(this, data); data.hash = Utils$1.hash([data.season_number, data.season_number > 10 ? ':' : '', data.episode_number, data.card ? data.card.original_name : data.original_name].join('')); var out_air = new Date((data.air_date + '').replace(/-/g, '/')); var out_now = Date.now(); var out_day = data.air_date ? Math.round((out_air.getTime() - out_now) / (24 * 60 * 60 * 1000)) : 1; data.left_days = out_day > 0 ? out_day : 0; data.left_text = Lang.translate('full_episode_days_left') + ': ' + (data.air_date ? out_day : '- -'); data.timeline = Timeline$1.view(data.hash); data.time = Utils$1.secondsToTime(data.runtime * 60, true); data.title = data.name || Lang.translate('torrent_serial_episode') + ' ' + data.episode_number; data.date = data.air_date ? Utils$1.parseTime(data.air_date).full : '----'; data.num = data.episode_number; return _this; } return Episode; }(Constructor(EpisodeMap)); var Helper$a = new Mask(Arrays.getKeys(EpisodeMap)); Helper$a.MASK.base = Helper$a.only('Mark'); var data$2 = []; var object$1 = false; var limit$3 = 300; var time_recent = 1000 * 60 * 60 * 24 * 14; // 14 дней var time_favorites = 1000 * 60 * 10; var time_extract = 1000 * 30; var time_season = 1000 * 60 * 60 * 24 * 7; // 7 дней /** * Запуск */ function init$_() { data$2 = Storage.cache('timetable', limit$3, []); Timer.add(time_favorites, favorites); Timer.add(time_extract, extract); // Добавляем поле ssn для хранения времени обновления сезонов data$2.forEach(function (a) { a.ssn = a.ssn || 0; }); // Обнуляем эпизоды, будем подгружать из db data$2.forEach(function (a) { a.episodes = []; }); Lampa.Listener.follow('state:changed', function (e) { if (e.target == 'favorite' && e.reason == 'update' && (e.method == 'add' || e.method == 'added') && e.type !== 'history') { console.log('Timetable', 'favorite changed:', e.reason, e.type, e.card.id); if (e.card.original_name && (e.card.source == 'tmdb' || e.card.source == 'cub')) update$a(e.card); } }); Favorite.listener.follow('remove', function (e) { if (e.card.original_name && e.method == 'id') { var find = data$2.find(function (a) { return a.id == e.card.id; }); if (find) { Arrays.remove(data$2, find); saveData(); Storage.remove('timetable', find.id); } } }); Lampa.Listener.follow('worker_storage', function (e) { if (e.type == 'insert' && e.name == 'timetable') { data$2 = Storage.get('timetable', '[]'); } }); // Начальный импорт из закладок favorites(); loadEpisodes(); ContentRows.add({ name: 'timetable_lately', title: Lang.translate('title_upcoming_episodes'), index: 1, screen: ['main', 'category'], call: function call(params, screen) { if (screen == 'category' && params.url == 'movie') return; var results = lately().slice(0, 20); if (!results.length) return; return function (call) { results.forEach(function (item) { item.params = { createInstance: function createInstance(item) { return new Episode(item); }, module: Helper$a.only('Card', 'Callback'), emit: { onlyEnter: router.call.bind(router, 'full', item.card), onlyFocus: function onlyFocus() { Background.change(Utils$1.cardImgBackgroundBlur(item.card)); } } }; Arrays.extend(item, item.episode); }); call({ results: results, title: Lang.translate('title_upcoming_episodes') }); }; } }); ContentRows.add({ name: 'timetable_recently', title: Lang.translate('title_recent_episodes'), index: 1, screen: ['main', 'category'], call: function call(params, screen) { if (screen == 'category' && params.url == 'movie') return; var results = recently().slice(0, 20); if (!results.length) return; return function (call) { results.forEach(function (item) { item.params = { createInstance: function createInstance(item) { return new Episode(item); }, module: Helper$a.only('Card', 'Callback'), emit: { onlyEnter: router.call.bind(router, 'full', item.card), onlyFocus: function onlyFocus() { Background.change(Utils$1.cardImgBackgroundBlur(item.card)); } } }; Arrays.extend(item, item.episode); }); call({ results: results, title: Lang.translate('title_recent_episodes') }); }; } }); } /** * Загрузить эпизоды из кеша * @returns {void} */ function loadEpisodes() { Cache.getData('timetable').then(function (all_data) { if (all_data) { all_data.forEach(function (obj) { var find = data$2.find(function (d) { return d.id == obj.id; }); if (find) find.episodes = obj.episodes || []; }); console.log('Timetable', 'load episodes from cache:', all_data.length); } })["catch"](function (e) { console.log('Timetable', 'load episodes from cache error:', e.message); }); } /** * Добавить карточки к парсингу * @param {[{id:integer,number_of_seasons:integer}]} elems - карточки */ function add$9(elems, log_type) { var filtred = elems.filter(function (elem) { return elem.original_name && typeof elem.id == 'number' && (elem.source == 'tmdb' || elem.source == 'cub'); }); console.log('Timetable', 'add:', elems.length, 'filtred:', filtred.length, 'type:', log_type || 'unknown'); filtred.forEach(function (elem) { var find = data$2.find(function (a) { return a.id == elem.id; }); if (!find) { data$2.push({ id: elem.id, season: elem.number_of_seasons || 0, episodes: [], ssn: 0 }); } }); saveData(); } /** * Сохранить данные без эпизодов * @returns {void} */ function saveData() { var clear_data = Arrays.clone(data$2); clear_data.forEach(function (a) { a.episodes = []; }); Storage.set('timetable', clear_data); } /** * Добавить из закладок */ function favorites() { var category = ['like', 'wath', 'book', 'look', 'viewed', 'scheduled', 'continued']; category.forEach(function (a) { add$9(Favorite.get({ type: a }), a); }); } function filter$1(episodes) { var filtred = []; var fileds = ['air_date', 'season_number', 'episode_number', 'name', 'still_path']; episodes.forEach(function (episode) { var item = {}; fileds.forEach(function (field) { if (typeof episode[field] !== 'undefined') item[field] = episode[field]; }); filtred.push(item); }); return filtred; } /** * Парсим карточку */ function parse$a(to_database) { var check = Favorite.check(object$1); var any = Favorite.checkAnyNotHistory(check); if (any || to_database) { // Если нет сезонов или давно не обновляли количество сезонов if (!object$1.season || Date.now() - object$1.ssn > time_season) { console.log('Timetable', 'parse:', object$1.id, 'old season:', object$1.season); TMDB.get('tv/' + object$1.id, {}, function (json) { object$1.season = Utils$1.countSeasons(json) || 1; object$1.ssn = Date.now(); parse$a(to_database); }, save$3, { life: 60 * 24 * 3 }); } else { console.log('Timetable', 'parse:', object$1.id, 'new season:', object$1.season); TMDB.get('tv/' + object$1.id + '/season/' + object$1.season, {}, function (ep) { if (!ep.episodes) return save$3(); object$1.episodes = filter$1(ep.episodes_original || ep.episodes); object$1.next = getNextEpisode(object$1.episodes); Cache.getData('timetable', object$1.id).then(function (obj) { if (obj) obj.episodes = object$1.episodes;else obj = Arrays.clone(object$1); Cache.rewriteData('timetable', object$1.id, obj).then(function () {})["catch"](function () {}); Lampa.Listener.send('state:changed', { target: 'timetable', reason: 'parse', id: object$1.id }); })["catch"](function (e) {}); save$3(); }, save$3, { life: 60 * 24 * 3 }); } } else { console.log('Timetable', 'remove:', object$1.id, 'not in favorites anymore'); Arrays.remove(data$2, object$1); Storage.remove('timetable', object$1.id); save$3(); } } /** * Получить следующий эпизод из списка * @param {[{air_date:string}]} episodes - эпизоды * @returns {object|boolean} */ function getNextEpisode(episodes) { var now_date = new Date(); now_date.setHours(0, 0, 0); var now_time = now_date.getTime(); var now_year = now_date.getFullYear(); var nxt_year = now_year + 1; var any = episodes.filter(function (ep) { if (ep.air_date) { if (ep.air_date.indexOf(now_year) === 0 || ep.air_date.indexOf(nxt_year) === 0) { return Lampa.Utils.parseToDate(ep.air_date).getTime() >= now_time; } } }); return any.length ? any[0] : false; } /** * Получить карточку для парсинга */ function extract() { var ids = data$2.filter(function (e) { return !e.scaned; }); console.log('Timetable', 'extract:', ids.length, 'total:', data$2.length); if (ids.length) { object$1 = ids[0]; parse$a(); } else { data$2.forEach(function (a) { return a.scaned = 0; }); } saveData(); } /** * Сохранить состояние */ function save$3() { if (object$1) { object$1.scaned = 1; object$1.scaned_time = Date.now(); saveData(); } } /** * Получить эпизоды для карточки если есть * @param {{id:integer}} elem - карточка * @returns {array} */ function get$b(elem, callback) { var fid = data$2.filter(function (e) { return e.id == elem.id; }); var res = (fid.length ? fid[0] : {}).episodes || []; if (typeof callback == 'function') { if (res.length) return callback(res); Cache.getData('timetable', elem.id).then(function (obj) { callback(obj ? obj.episodes || [] : [], true); })["catch"](function (e) { callback(res, true); }); } else { return res; } } /** * Добавить карточку в парсинг самостоятельно * @param {{id:integer,number_of_seasons:integer}} elem - карточка */ function update$a(elem) { if (elem.original_name && typeof elem.id == 'number' && (elem.source == 'tmdb' || elem.source == 'cub')) { var check = Favorite.check(elem); var any = Favorite.checkAnyNotHistory(check); var id = data$2.filter(function (a) { return a.id == elem.id; }); var item = { id: elem.id, season: elem.number_of_seasons ? Utils$1.countSeasons(elem) : 0, episodes: [], ssn: Date.now() }; if (any) { if (!id.length) { console.log('Timetable', 'push:', elem.id); data$2.push(item); saveData(); object$1 = item; } else { object$1 = id[0]; object$1.season = elem.number_of_seasons ? Utils$1.countSeasons(elem) : object$1.season || 0; } parse$a(); } else { object$1 = item; parse$a(true); } } } /** * Получить все данные * @returns {[{id:integer,season:integer,episodes:[]}]} */ function all$3() { return data$2; } function lately() { var fav = favoriteCards(); var cards = []; data$2.filter(function (d) { return fav.find(function (c) { return c.id == d.id; }); }).forEach(function (season) { var episode = season.episodes.length ? getNextEpisode(season.episodes) : season.next; if (episode) { // Проверка season.next на актуальность if (Lampa.Utils.parseToDate(episode.air_date).getTime() >= Date.now()) { cards.push({ card: Arrays.clone(fav.find(function (c) { return c.id == season.id; })), episode: episode, time: Lampa.Utils.parseToDate(episode.air_date).getTime(), season: season }); } } }); cards = cards.sort(function (a, b) { if (a.time > b.time) return 1;else if (a.time < b.time) return -1;else return 0; }); return cards; } function recently() { var fav = favoriteCards(); var now = new Date(); now.setHours(0, 0, 0, 0); var now_time = now.getTime(); var start_time = now_time - time_recent; var cards = []; data$2.filter(function (d) { return fav.find(function (c) { return c.id == d.id; }); }).forEach(function (season) { if (!season.episodes || !season.episodes.length) return; var card = Arrays.clone(fav.find(function (c) { return c.id == season.id; })); season.episodes.forEach(function (episode) { if (!episode.air_date) return; var air_time = Utils$1.parseToDate(episode.air_date).getTime(); if (air_time >= start_time && air_time <= now_time) { var viewed = Timeline$1.watchedEpisode(card, episode.season_number, episode.episode_number); if (viewed < 60) { cards.push({ card: card, episode: episode, viewed: viewed, time: air_time, season: season }); } } }); }); cards = cards.sort(function (a, b) { if (a.time > b.time) return -1;else if (a.time < b.time) return 1;else return 0; }); return cards; } function favoriteCards() { var fav = Favorite.full().card; if (Account$1.Permit.sync) fav = Account$1.Bookmarks.all(); fav = fav.filter(function (f) { return f.original_name && (f.source == 'tmdb' || f.source == 'cub'); }); return fav; } var TimeTable = { init: init$_, get: get$b, add: add$9, all: all$3, update: update$a, lately: lately, recently: recently }; var Watched = { onCreate: function onCreate() { var _this = this; var timer; this.html.on('hover:focus hover:touch hover:hover', function () { clearTimeout(timer); timer = setTimeout(function () { _this.html.classList.contains('focus') && _this.emit('watched'); }, 500); }); this.listenerWatched = function (e) { if (e.target == 'timeline' && e.reason == 'read' || e.target == 'timetable' && e.id == _this.data.id) _this.emit('update'); }; Lampa.Listener.follow('state:changed', this.listenerWatched); }, onUpdate: function onUpdate() { var _this$watched_wrap; this.watched_checked = false; (_this$watched_wrap = this.watched_wrap) === null || _this$watched_wrap === void 0 ? void 0 : _this$watched_wrap.remove(); this.html.classList.contains('focus') && this.emit('watched'); }, onWatched: function onWatched() { var _this2 = this; if (!Storage.field('card_episodes')) return; if (!this.watched_checked) { var get = function get(callback) { if (data.original_name) TimeTable.get(data, callback);else callback([]); }; var data = this.data; get(function (episodes, from_db) { var viewed; // Нужно разбить 1й сезон на сезоны и взять последний сезон if (episodes[0] && episodes[0].season_number == 1) { var seasons = Utils$1.splitEpisodesIntoSeasons(episodes); episodes = seasons[Object.keys(seasons).pop()]; } var Draw = function Draw() { episodes.forEach(function (ep) { var hash = Utils$1.hash([ep.season_number, ep.season_number > 10 ? ':' : '', ep.episode_number, data.original_title].join('')); var view = Timeline$1.view(hash); if (view.percent) viewed = { ep: ep, view: view }; }); // Пытаемся найти последний просмотренный из истории последнего просмотра if (!viewed && data.original_name) { var last = Storage.get('online_watched_last', '{}'); var filed = last[Utils$1.hash(data.original_title)]; if (filed && filed.episode) { viewed = { ep: { episode_number: filed.episode, name: Lang.translate('full_episode') + ' ' + filed.episode }, view: Timeline$1.view(Utils$1.hash([filed.season, filed.season > 10 ? ':' : '', filed.episode, data.original_title].join(''))) }; } } // Если это фильм и не нашли просмотренное, то проверим по прогрессу просмотра из таймлайна if (!viewed && !data.original_name) { var time = Timeline$1.watched(data, true); if (time.percent) { viewed = { ep: { name: Lang.translate('title_viewed') + ' ' + (time.time ? Utils$1.secondsToTimeHuman(time.time) : time.percent + '%') }, view: time }; } } // Если это сериал и не нашли просмотренное, то проверим по прогрессу просмотра из таймлайна if (!viewed && data.original_name) { var any = Timeline$1.watched(data, true).pop(); if (any) viewed = { ep: { name: Lang.translate('full_episode') + ' ' + any.ep }, view: any.view }; } if (viewed) { var soon = []; var start = episodes.indexOf(viewed.ep); // Эпизод из истории или из таймлайна не входит в список последнего сезона var next = (start >= 0 ? episodes.slice(start) : []).filter(function (ep) { return ep.air_date; }).filter(function (ep) { var date = Utils$1.parseToDate(ep.air_date).getTime(); if (date > Date.now()) soon.push(ep); return date < Date.now(); }).slice(0, 5); if (next.length == 0) next = [viewed.ep]; if (soon.length && next.length < 5 && !next.find(function (n) { return n.episode_number == soon[0].episode_number; })) next.push(soon[0]); var wrap = Template.js('card_watched', {}); next.forEach(function (ep) { var div = document.createElement('div'); var span = document.createElement('span'); var date = Utils$1.parseToDate(ep.air_date); var now = Date.now(); var days = Math.ceil((date.getTime() - now) / (24 * 60 * 60 * 1000)); div.addClass('card-watched__item'); div.append(span); span.innerText = (ep.episode_number ? ep.episode_number + ' - ' : '') + (days > 0 ? Lang.translate('full_episode_days_left') + ': ' + days : ep.name || Lang.translate('noname')); if (ep == viewed.ep) div.append(Timeline$1.render(viewed.view)[0]); wrap.find('.card-watched__body').append(div); }); _this2.watched_wrap = wrap; var view = _this2.html.find('.card__view'); view.insertBefore(wrap, view.firstChild); } }; Draw(); }); this.watched_checked = true; } }, onDestroy: function onDestroy() { Lampa.Listener.remove('state:changed', this.listenerWatched); } }; var Style = { onCreate: function onCreate() { if (this.params.style.name == 'collection') { var _this$html$find; this.html.addClass('card--collection'); (_this$html$find = this.html.find('.card__age')) === null || _this$html$find === void 0 ? void 0 : _this$html$find.remove(); } if (this.params.style.name == 'wide') { var _this$html$find2, _this$html$find3; (_this$html$find2 = this.html.find('.card__title')) === null || _this$html$find2 === void 0 ? void 0 : _this$html$find2.remove(); (_this$html$find3 = this.html.find('.card__age')) === null || _this$html$find3 === void 0 ? void 0 : _this$html$find3.remove(); this.html.addClass('card--wide'); this.data.poster = this.data.cover; var promo_wrap = Template.elem('div', { "class": 'card__promo' }); if (this.data.title || this.data.name) { promo_wrap.append(Template.elem('div', { "class": 'card__promo-title', text: this.data.title || this.data.name })); } if (this.data.overview) { promo_wrap.append(Template.elem('div', { "class": 'card__promo-text', text: this.data.overview.slice(0, 110) + (this.data.overview.length > 110 ? '...' : '') })); } this.html.find('.card__view').append(promo_wrap); } } }; var html$g = null; /** * Показать подсказку по управлению * @param {Object} params - Параметры * @param {string} params.name - Имя для кеша * @param {string} params.text - Текст подсказки * @param {string} params.button - Кнопка для подсветки (ok, up, down, left, right) * @param {number} params.interval - Интервал показа в минутах (по умолчанию 7 дней) * @return {void} */ function show$7() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (html$g || !Platform.tv()) return; Arrays.extend(params, { name: 'none', text: Lang.translate('remote_helper_long'), button: 'ok', interval: 60 * 24 * 7 // week }); var cached_time = Storage.get('remote_helper', '{}'); if (cached_time[params.name] && cached_time[params.name] + 1000 * 60 * params.interval > Date.now()) return; html$g = Template.js('remote_helper', params); html$g.addClass('highlight--' + params.button); document.body.appendChild(html$g); setTimeout(function () { html$g.addClass('active'); setTimeout(function () { html$g.removeClass('active'); setTimeout(function () { html$g.remove(); html$g = null; }, 500); }, 10000); }, 10); Storage.set('remote_helper', _objectSpread2(_objectSpread2({}, cached_time), {}, _defineProperty({}, params.name, Date.now()))); } var RemoteHelper = { show: show$7 }; var Menu = { onInit: function onInit() { this.menu_list = []; }, onCreate: function onCreate() { var _this = this; this.html.on('hover:long', function () { var enabled = Controller.enabled().name; var menu = []; _this.menu_list.forEach(function (item, i) { i !== 0 && menu.push({ title: item.title, separator: true }); menu = menu.concat(item.menu()); }); if (!menu.length) return; _this.emit('menu', menu, _this.html, _this.data); Select.show({ title: Lang.translate('title_action'), items: menu, onBack: function onBack() { Controller.toggle(enabled); }, onBeforeClose: function onBeforeClose() { Controller.toggle(enabled); return true; } }); }); this.html.on('hover:focus', function () { if (window.app_time_end < Date.now() - 20000) RemoteHelper.show({ name: 'card_menu' }); }); } }; var Plugins$1 = { onCreate: function onCreate() { function drawMenu() { var _this = this; var menu = []; object$2.plugins.forEach(function (plugin) { if (plugin.type == 'video' && plugin.onContextMenu && plugin.onContextLauch) { menu.push({ title: plugin.name, subtitle: plugin.subtitle || plugin.description, onSelect: function onSelect() { if (!_this.data.imdb_id && _this.data.source == 'tmdb') { Loading$1.start(function () { Loading$1.stop(); Controller.toContent(); }); TMDB.external_imdb_id({ type: _this.data.name ? 'tv' : 'movie', id: _this.data.id }, function (imdb_id) { Loading$1.stop(); _this.data.imdb_id = imdb_id; plugin.onContextLauch(_this.data); }); } else plugin.onContextLauch(_this.data); } }); } }); return menu; } this.menu_list.push({ title: Lang.translate('settings_main_plugins'), menu: drawMenu.bind(this) }); } }; var Subscribe = { onCreate: function onCreate() { if (this.data.subscribe) { var data = this.data.subscribe; var sube = Template.elem('div', { "class": 'card__subscribe', children: [Template.elem('div', { "class": 'card__subscribe-status ' + (data.status ? 'on' : 'off') }), Template.elem('div', { "class": 'card__subscribe-position', text: 'S' + data.season + ' E' + data.episode }), Template.elem('div', { "class": 'card__subscribe-voice', text: data.voice })] }); this.html.find('.card__view').after(sube); } } }; var Lgbt = { onVisible: function onVisible() { var lgbt_block = Storage.field('lgbt_content_block') || vpn.is(['ru', 'by']); var lgbt_key = this.data.id + '_' + (this.data.first_air_date ? 'tv' : 'movie'); var img = this.html.find('.card__img'); if (img && lgbt_block && window.lampa_settings.lgbt && window.lampa_settings.lgbt[lgbt_key]) { var pattern = Color.circlePattern(20); var layer = Template.elem('div', { "class": 'card__filter' }); layer.style.backgroundImage = 'url(' + pattern + ')'; img.after(layer); } } }; var CardMap = { Card: Card$2, Folder: Folder, Icons: Icons, Ratting: Ratting, Release: Release, Plugins: Plugins$1, Favorite: Favorite$1, Watched: Watched, Lgbt: Lgbt, Subscribe: Subscribe, Style: Style, Menu: Menu, Callback: Callback$1 }; var Helper$9 = new Mask(Arrays.getKeys(CardMap)); Helper$9.MASK.base = Helper$9.toggle(Helper$9.MASK.all, 'Folder', 'Subscribe'); var data$1 = {}; var listener$b = Subscribe$2(); var category$2 = ['like', 'wath', 'book', 'history', 'look', 'viewed', 'scheduled', 'continued', 'thrown']; var marks = ['look', 'viewed', 'scheduled', 'continued', 'thrown']; /** * Запуск */ function init$Z() { read$1(); ContentRows.add({ name: 'continue_watch', title: Lang.translate('title_continue'), index: 1, screen: ['main', 'category'], call: function call(params, screen) { var media = screen == 'main' ? 'tv' : params.url; var results = continues(media); if (!results.length) return; return function (call) { // Смотрим есть ли новые серии с переводом if (media == 'tv' || media == 'anime') { var cub_notices = Notice$1.get('cub').items(); cub_notices = cub_notices.filter(function (n) { return n.item.method == 'tv-voice'; }); // Получаем из истории только те карточки которые есть в уведомлениях var history = get$a({ type: 'history' }).filter(function (h) { return cub_notices.find(function (n) { return n.item.card_id == h.id; }); }); // Фильтруем только те карточки у которых есть новые серии var new_episode = history.map(function (h) { var noty = cub_notices.find(function (n) { return n.item.card_id == h.id; }); var card = Arrays.clone(h); card.params = { module: Helper$9.toggle(Helper$9.MASK.base, 'Subscribe') }; card.subscribe = { status: 1, season: noty.item.season, episode: noty.item.episode, voice: noty.data.voice }; card.viewed = Timeline$1.watchedEpisode(h, noty.item.season, noty.item.episode); return card; }); // Оставляем только те у которых просмотр меньше 10% new_episode = new_episode.filter(function (n) { return n.viewed < 10; }); new_episode = new_episode.filter(function (e) { var jpan = Utils$1.containsJapanese(e.original_name || e.name || '') || e.original_language == 'ja'; return media == 'anime' ? jpan : !jpan; }); if (new_episode.length) { // Убираем из основного списка карточки у которых есть новые серии results = results.filter(function (r) { return !new_episode.find(function (h) { return h.id == r.id; }); }); results = [].concat(new_episode, results); // Оставляем не более 20 карточек results = results.slice(0, 19); } } call({ results: results, title: media == 'tv' || media == 'anime' ? Lang.translate('title_continue') : Lang.translate('title_watched') }); }; } }); } /** * Сохранить */ function save$2() { Storage.set('favorite', data$1); } /** * Добавить * @param {String} where * @param {Object} card */ function add$8(where, card, limit) { if (Account$1.Permit.sync) { listener$b.send('add', { where: where, card: card }); } else { var find = data$1[where].find(function (id) { return id == card.id; }); if (!find) { Arrays.insert(data$1[where], 0, card.id); listener$b.send('add', { where: where, card: card }); if (!search$4(card.id)) data$1.card.push(Utils$1.clearCard(Arrays.clone(card))); if (limit) { var excess = data$1[where].slice(limit); for (var i = excess.length - 1; i >= 0; i--) { remove$2(where, { id: excess[i] }); } } save$2(); } else { Arrays.remove(data$1[where], card.id); Arrays.insert(data$1[where], 0, card.id); save$2(); listener$b.send('added', { where: where, card: card }); } Lampa.Listener.send('state:changed', { target: 'favorite', reason: 'update', method: !find ? 'add' : 'added', type: where, card: card }); } } /** * Удалить * @param {String} where * @param {Object} card */ function remove$2(where, card) { if (Account$1.Permit.sync) { listener$b.send('remove', { where: where, card: card, method: 'id' }); } else { Arrays.remove(data$1[where], card.id); listener$b.send('remove', { where: where, card: card, method: 'id' }); for (var i = data$1.card.length - 1; i >= 0; i--) { var element = data$1.card[i]; if (!check$3(element).any) { Arrays.remove(data$1.card, element); listener$b.send('remove', { where: where, card: element, method: 'card' }); } } save$2(); Lampa.Listener.send('state:changed', { target: 'favorite', reason: 'update', method: 'remove', type: where, card: card }); } } /** * Найти * @param {integer} id * @returns Object */ function search$4(id) { var found; for (var index = 0; index < data$1.card.length; index++) { var element = data$1.card[index]; if (element.id == id) { found = element; break; } } return found; } /** * Переключить * @param {String} where * @param {Object} card */ function toggle$6(where, card) { var find = cloud(card); if (marks.find(function (a) { return a == where; })) { var added = marks.find(function (a) { return find[a]; }); if (added && added !== where) remove$2(added, card); } if (find[where]) remove$2(where, card);else add$8(where, card); return find[where] ? false : true; } /** * Проверить * @param {Object} card * @returns Object */ function check$3(card) { var result = { any: false }; category$2.forEach(function (a) { result[a] = data$1[a].find(function (id) { return id == card.id; }); if (result[a]) result.any = true; }); return result; } /** * Проверить есть ли карточка где либо кроме истории * @param {Object} status * @returns {Boolean} */ function checkAnyNotHistory(status) { var any = false; category$2.filter(function (a) { return a !== 'history'; }).forEach(function (a) { if (status[a]) any = true; }); return any; } /** * Облако, закладки из cub * @param {Object} card * @returns {Object} */ function cloud(card) { if (Account$1.Permit.sync) { var result = { any: false }; category$2.forEach(function (a) { result[a] = Boolean(Account$1.Bookmarks.find({ type: a, id: card.id })); if (result[a]) result.any = true; }); return result; } else return check$3(card); } /** * Получить списаок по типу * @param {String} params.type - тип * @returns Object */ function get$a(params) { if (Account$1.Permit.sync) { return Account$1.Bookmarks.get(params); } else { var result = []; var ids = data$1[params.type]; ids.forEach(function (id) { for (var i = 0; i < data$1.card.length; i++) { var card = data$1.card[i]; if (card.id == id) result.push(card); } }); return result; } } /** * Очистить * @param {String} where * @param {Object} card */ function clear$9(where, card) { if (Account$1.Permit.sync) { Account$1.Bookmarks.clear(where); } else { if (card) remove$2(where, card);else { for (var i = data$1[where].length - 1; i >= 0; i--) { var _card = search$4(data$1[where][i]); if (_card) remove$2(where, _card); } } } } /** * Считать последние данные * @param {Boolean} nolisten - не посылать событие изменения состояния */ function read$1() { var nolisten = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; data$1 = Storage.get('favorite', '{}'); var empty = { card: [] }; category$2.forEach(function (a) { empty[a] = []; }); Arrays.extend(data$1, empty); if (nolisten) return; Lampa.Listener.send('state:changed', { target: 'favorite', reason: 'read' }); } /** * Получить весь список что есть */ function full$2() { var empty = { card: [] }; category$2.forEach(function (a) { empty[a] = []; }); Arrays.extend(data$1, empty); return data$1; } function all$2() { var result = {}; category$2.forEach(function (a) { result[a] = get$a({ type: a }); }); return result; } function continues(type) { var result = get$a({ type: 'history' }); var viewed = get$a({ type: 'viewed' }); var thrown = get$a({ type: 'thrown' }); // Убираем из продолжить то что уже полностью просмотрено result = result.filter(function (e) { return !viewed.find(function (v) { return v.id == e.id; }) && !thrown.find(function (t) { return t.id == e.id; }); }); result = result.filter(function (e) { var is_tv = e.number_of_seasons || e.first_air_date; var jpan = e.original_language == 'ja'; if (type == 'anime') return is_tv && (Utils$1.containsJapanese(e.original_name || e.name || '') || jpan);else if (type == 'tv') return is_tv && !(Utils$1.containsJapanese(e.original_name || e.name || '') || jpan);else return !is_tv; }); return Arrays.clone(result.slice(0, 19)); } var Favorite = { listener: listener$b, init: Utils$1.onceInit(init$Z), check: cloud, add: add$8, remove: remove$2, toggle: toggle$6, get: get$a, clear: clear$9, continues: continues, full: full$2, checkAnyNotHistory: checkAnyNotHistory, all: all$2, read: read$1 }; var Listener$1 = Subscribe$2(); var html$f; var destroyed = false; /** * Отображает прогресс загрузки приложения * @returns {void} */ function init$Y() { html$f = $("
    \n
    \n
    \n
    "); $('body').append(html$f); step(0); } /** * Обновляет шаг загрузки * @param {number} position - номер шага */ function step(position) { if (destroyed) return; html$f.find('.lp-step').text(Lang.translate('loading_progress_step_' + position)); console.log('LoadingProgress', 'step:', position, Lang.translate('loading_progress_step_' + position)); } /** * Обновляет статус загрузки * @param {string} text - текст статуса */ function status$1(text) { if (destroyed) return; html$f.find('.lp-status').text(text); console.log('LoadingProgress', 'status:', text); } /** * Удаляет прогресс загрузки * @returns {void} */ function destroy$b() { destroyed = true; html$f.remove(); } var LoadingProgress = { init: init$Y, step: step, status: status$1, destroy: destroy$b }; var bookmarks = []; // имеет вид [{id, cid, card_id, type, data, profile, time},...] var bookmarks_map = {}; // имеет вид {type: {card_id: bookmark, ...}, ...} var tracker_name = 'account_bookmarks_sync'; var tracker_data = { version: 0, time: 0 }; var update_timer; var push_queue = []; var push_running = false; function processQueue() { if (push_running || push_queue.length === 0) return; push_running = true; var _push_queue$shift = push_queue.shift(), method = _push_queue$shift.method, type = _push_queue$shift.type, card = _push_queue$shift.card; var find = bookmarks.find(function (elem) { return elem.card_id == card.id && elem.type == type; }); Api$1.load('bookmarks/' + method, {}, { type: type, data: JSON.stringify(Utils$1.clearCard(Arrays.clone(card))), card_id: card.id, id: find ? find.id : 0 }).then(function () { clearTimeout(update_timer); update_timer = setTimeout(function () { update$9(function () { Socket.send('bookmarks', {}); Lampa.Listener.send('state:changed', { target: 'favorite', reason: 'update', method: method, card: card, type: type }); }); }, 500); })["catch"](function () { console.warn('Account', 'bookmarks ' + method + ' fail'); })["finally"](function () { push_running = false; processQueue(); }); } /** * Запуск * @return {void} */ function init$X() { Favorite.listener.follow('add,added', function (e) { push$7('add', e.where, e.card); }); Favorite.listener.follow('remove', function (e) { if (e.method == 'id') push$7('remove', e.where, e.card); }); Storage.listener.follow('change', function (e) { if (e.name == 'protocol' || e.name == 'account_use' && e.value == 'true') update$9(function () { Lampa.Listener.send('state:changed', { target: 'favorite', reason: 'read' }); }); }); Listener$1.follow('profile_select', function () { bookmarks = []; bookmarks_map = {}; // Сбрасываем трекер чтобы при смене профиля сразу получить дамп закладок tracker_data.time = 0; tracker_data.version = 0; Cache.rewriteData('other', tracker_name, tracker_data)["catch"](function (e) { console.log('Account', 'bookmarks tracker cache not reset', e.message); })["finally"](function () { update$9(function () { Lampa.Listener.send('state:changed', { target: 'favorite', reason: 'profile' }); }); }); }); Timer.add(1000 * 60 * 5, function () { update$9(); }); } /** * Добавить/Удалить закладку * @param {String} method - add/remove * @param {String} type - тип закладки * @param {Object} card - карточка * @return {void} */ function push$7(method, type, card) { if (permit.sync) { push_queue.push({ method: method, type: type, card: card }); processQueue(); } } /** * Сохранение закладок в кэш * @param {number} version - версия закладок на сервере * @return {void} */ function saveToCache(version) { Cache.rewriteData('other', 'account_bookmarks_' + permit.account.profile.id, bookmarks).then(function () { tracker_data.version = version; tracker_data.time = Date.now(); Cache.rewriteData('other', tracker_name, tracker_data)["catch"](function (e) {}); })["catch"](function (e) { console.log('Account', 'bookmarks cache not saved', e.message); }); } /** * Загрузка закладок из кэша * @param {function} call - вызов по окончании * @return {void} */ function loadFromCache(call) { if (bookmarks.length) return call && call(); Cache.getData('other', 'account_bookmarks_' + permit.account.profile.id).then(function (data) { bookmarks = data && data.length ? data : []; console.log('Account', 'bookmarks cache load:', bookmarks.length, 'data:', data ? 'yes' : 'no'); createMap(); if (call) call(); })["catch"](function () { console.log('Account', 'bookmarks cache not load'); if (call) call(); }); } function loadTrackerData(call) { Cache.getDataAnyCase('other', tracker_name).then(function (data) { if (data) { tracker_data.version = data.version || 0; tracker_data.time = data.time || 0; } if (call) call(); })["catch"](function () { if (call) call(); }); } /** * Загрузка и обновление закладок * @param {function} call - вызов по окончании * @return {void} */ function update$9(call) { if (permit.sync) { console.log('Account', 'sync enabled, updating bookmarks'); LoadingProgress.status('Bookmarks start sync'); loadTrackerData(function () { // Если с момента последнего обновления прошло больше 15 дней, то загружаем дамп if (tracker_data.time < Date.now() - 1000 * 60 * 60 * 24 * 15) { console.log('Account', 'bookmarks start full update', tracker_data.version); LoadingProgress.status('Bookmarks loading full dump'); Api$1.load('bookmarks/dump', { dataType: 'text' }).then(function (result) { LoadingProgress.status('Bookmarks parsing dump'); // !!! WebWorker почему-то зависает на телевизорах и вылетает, пока без него // Парсим текст в массив закладок // WebWorker.json({ // type: 'parse', // data: result // },(e)=>{ console.log('Account', 'bookmarks dump load complete, size:', result.length); var e = { data: Arrays.decodeJson(result, {}) }; if (!e.data.bookmarks) { console.error('Account', 'bookmarks wrong dump format', Utils$1.shortText(result, 300)); if (call && typeof call == 'function') call(); return; } console.log('Account', 'bookmarks full update complete, total:', e.data.bookmarks.length); // Переводим строки с .data в объект, обновляем локальный кэш и карту rawToCard(e.data.bookmarks, function () { saveToCache(e.data.version); if (call && typeof call == 'function') call(); }); //}) })["catch"](function (e) { LoadingProgress.status('Bookmarks no dump load, trying cache'); console.error('Account', 'bookmarks full update fail, trying load from cache:', 'message', e.message); loadFromCache(function () { if (call && typeof call == 'function') call(); }); }); } // Иначе получаем только изменения с последней версии else { console.log('Account', 'bookmarks start update since', tracker_data.version); LoadingProgress.status('Bookmarks load from cache'); loadFromCache(function () { LoadingProgress.status('Bookmarks loading changelog'); Api$1.load('bookmarks/changelog?since=' + tracker_data.version).then(function (result) { LoadingProgress.status('Bookmarks applying changelog'); result.changelog.forEach(function (change) { if (change.action == 'remove') { var _find = bookmarks.find(function (book) { return book.id == change.entity_id; }); if (_find) Arrays.remove(bookmarks, _find); } else if (change.action == 'update') { var _find2 = bookmarks.find(function (book) { return book.id == change.entity_id; }); if (_find2) { _find2.time = change.updated_at; Arrays.remove(bookmarks, _find2); Arrays.insert(bookmarks, 0, _find2); } } else if (change.action == 'add') { if (change.data) { change.data = Utils$1.clearCard(Arrays.decodeJson(change.data, {})); // Если закладка уже есть, то обновляем время и двигаем ее в начало, иначе просто добавляем // !!! Надеюсь это избавит от бага с дублированием закладок при обновлении var _find3 = bookmarks.find(function (book) { return book.id == change.entity_id; }); if (_find3) { _find3.time = change.updated_at; Arrays.remove(bookmarks, _find3); Arrays.insert(bookmarks, 0, _find3); } else { Arrays.insert(bookmarks, 0, change); } } } else if (change.action == 'clear') { var filter = bookmarks.filter(function (book) { return book.type == change.entity_id; }); filter.forEach(function (book) { return Arrays.remove(bookmarks, book); }); } }); // Сохраняем обновленные закладки в кэш saveToCache(result.version); // Обновляем карту createMap(); // Обновляем каналы на андроид тв updateChannels(); console.log('Account', 'bookmarks update complete to version', result.version, 'changes:', result.changelog.length, 'total:', bookmarks.length); if (call && typeof call == 'function') call(); })["catch"](function () { console.warn('Account', 'bookmarks update since fail'); if (call && typeof call == 'function') call(); }); }); } }); } else { console.log('Account', 'sync disabled'); rawToCard([], function () { if (call && typeof call == 'function') call(); }); } } /** * Очистка закладок * @param {string} where - группа закладок для очистки * @return {void} */ function clear$8(where) { if (permit.sync) { Api$1.load('bookmarks/clear', {}, { type: 'group', group: where }).then(function () { update$9(function () { // Оповещаем другие устройства о изменении закладок Socket.send('bookmarks', {}); // Глобальное оповещение об изменении закладок для обновления карточек Lampa.Listener.send('state:changed', { target: 'favorite', reason: 'clear', type: where }); }); })["catch"](function () { console.warn('Account', 'bookmarks clear fail'); }); } } /** * Получить закладки по типу * @param {object} params - {type} * @return {array} - [card, ...] */ function get$9(params) { return bookmarks.filter(function (elem) { return elem.type == params.type; }).map(function (elem) { return elem.data; }); } /** * Найти закладку по типу и id * @param {object} params - {type, id} * @return {object|null} - card или null */ function find$1(params) { var _bookmarks_map$params; return bookmarks_map[params.type] ? (_bookmarks_map$params = bookmarks_map[params.type][params.id]) === null || _bookmarks_map$params === void 0 ? void 0 : _bookmarks_map$params.data : null; } /** * Получить все закладки * @return {array} - [card, ...] */ function all$1() { return bookmarks.map(function (elem) { return elem.data; }); } /** * Обновление каналов на андроид тв * @return {void} */ function updateChannels() { if (Platform.is('android') && typeof AndroidJS.saveBookmarks !== 'undefined' && bookmarks.length) { AppWorker.json({ type: 'stringify', data: bookmarks }, function (j) { AndroidJS.saveBookmarks(j.data); }); } } /** * Преобразует row.data из строки в объект * @param {array} rows - массив закладок из БД * @param {function} call - вызов по окончании * @return {void} */ function rawToCard(rows, call) { AppWorker.utils({ type: 'account_bookmarks_parse', data: rows }, function (e) { bookmarks = Arrays.isArray(e.data) ? e.data : []; console.log('Account', 'bookmarks rawToCard complete, total:', bookmarks.length); createMap(); updateChannels(); if (call) call(); }); } /** * Создаем карту закладок для быстрого поиска * @return {void} */ function createMap() { bookmarks_map = {}; bookmarks.forEach(function (elem) { elem.data = Utils$1.clearCard(elem.data); if (!bookmarks_map[elem.type]) bookmarks_map[elem.type] = {}; bookmarks_map[elem.type][elem.card_id] = elem; }); } /** * Синхронизация закладок из локальных на сервер * @param {function} callback - вызов по окончании * @return {void} */ function sync$1(callback) { var file; try { file = new File([localStorage.getItem('favorite') || '{}'], "bookmarks.json", { type: "text/plain" }); } catch (e) { console.warn('Account', 'bookmarks file create error', e.message); } if (!file) { try { file = new Blob([localStorage.getItem('favorite') || '{}'], { type: 'text/plain' }); file.lastModifiedDate = new Date(); } catch (e) { Noty.show(Lang.translate('account_export_fail')); console.warn('Account', 'bookmarks blob create error', e.message); } } if (file) { var formData = new FormData($('
    ')[0]); formData.append("file", file, "bookmarks.json"); $.ajax({ url: Api$1.url() + 'bookmarks/sync', type: 'POST', data: formData, async: true, cache: false, contentType: false, enctype: 'multipart/form-data', processData: false, headers: { token: permit.token, profile: permit.account.profile.id }, success: function success(j) { if (j.secuses) { Noty.show(Lang.translate('account_sync_secuses')); update$9(); } else { console.error('Account', 'bookmarks sync fail response'); } callback && callback(); }, error: function error() { console.error('Account', 'bookmarks sync fail'); Noty.show(Lang.translate('account_export_fail')); callback && callback(); } }); } else { callback && callback(); console.error('Account', 'bookmarks no create file for sync'); } } var Bookmarks$1 = { init: Utils$1.onceInit(init$X), push: push$7, update: update$9, clear: clear$8, get: get$9, all: all$1, sync: sync$1, find: find$1 }; var profile_icon; function init$W() { profile_icon = Template.elem('div', { "class": 'head__action selector open--profile' }); profile_icon.on('hover:enter', select$2.bind(null, Controller.toggle.bind(Controller, 'head'))); Head.render().find('.full--screen').before(profile_icon); Storage.listener.follow('change', function (e) { if (e.name == 'account' || e.name == 'protocol') update$8(); }); ParentalControl.add('account_profiles', { title: 'account_profiles' }); if (!permit.token) update$8(); } /** * Обновляет иконку профиля * @returns {void} */ function update$8() { profile_icon.empty(); if (permit.token) { profile_icon.append(Template.elem('img')); Utils$1.imgLoad(profile_icon.find('img'), Utils$1.protocol() + object$2.cub_domain + '/img/profiles/' + (permit.account.profile.icon || 'l_1') + '.png', function () {}, function (img) { img.src = './img/img_load.svg'; }); } else { profile_icon.append(Template.js('icon_profile')); } } /** * Возвращает иконку профиля * @returns {string} */ function icon$2() { return permit.token ? Utils$1.protocol() + object$2.cub_domain + '/img/profiles/' + (permit.account.profile.icon || 'l_1') + '.png' : ''; } /** * Проверяет наличие профиля, если его нет - загружает * @param {function} call - вызывается если профиль есть * @returns {void} */ function check$2(call) { if (permit.access) { Api$1.load('profiles/all', { attempts: 3 }).then(function (result) { var account = permit.account; if (account.profile.id) { var active = result.profiles.find(function (p) { return p.id == account.profile.id; }); if (active) account.profile = active; } else { var main = result.profiles.find(function (p) { return p.main; }); if (main) account.profile = main; } Storage.set('account', account, true); // Переключение на детский источник Listener$1.send('profile_check', { profile: account.profile }); call(); })["catch"](call); } else { Storage.set('account_user', '', true); } } /** * Выбор профиля * @param {string} callback - вызывается после выбора * @returns {void} */ function select$2(callback) { ParentalControl.personal('account_profiles', function () { if (!permit.token) return Modal$1.account(); var account = permit.account; Loading$1.start(function () { Api$1.clear(); Loading$1.stop(); }); Api$1.load('profiles/all').then(function (result) { Loading$1.stop(); if (result.secuses) { var items = Arrays.clone(result.profiles); var clone = Arrays.clone(result.profiles); items.reverse(); clone.reverse(); var _select = []; var maximum = Lampa.Account.hasPremium() ? 8 : 3; var list = items.map(function (elem, index) { elem.title = elem.name; elem.template = 'selectbox_icon'; elem.icon = ''; elem.clone = clone[index]; elem.subtitle = elem.main ? Lang.translate('account_profile_main') : elem.child ? Lang.translate('account_profile_child') + ' ' + '(' + Lang.translate('filter_rating_to') + ' ' + (elem.age || 12) + ')' : ''; elem.selected = account.profile.id == elem.id; return elem; }); var additional = { title: Lang.translate('account_profile_add'), template: 'selectbox_icon', icon: '', ghost: list.length >= maximum, onSelect: function onSelect(a) { callback && callback(); if (a.ghost) { Noty.show(Lang.translate('account_profile_limited').replace('{count}', maximum)); } else { var contrioller = Controller.enabled().name; Input.edit({ title: Lampa.Lang.translate('account_profile_name'), value: '', free: true, nosave: true, nomic: true }, function (name) { Controller.toggle(contrioller); if (name) { name = Utils$1.capitalizeFirstLetter(name); Api$1.load('profiles/create', {}, { name: name }).then(function (result) { Bell.push({ text: Lang.translate('account_profile_created') }); })["catch"](function (e) { Noty.show(Lang.translate(e.decode_code == 200 ? 'account_profile_limited' : 'network_error').replace('{count}', maximum)); }); } }); } } }; _select = [additional].concat(list); Select.show({ title: Lang.translate('account_profiles'), items: _select, onSelect: function onSelect(a) { account.profile = a.clone; Storage.set('account', account); callback && callback(); Listener$1.send('profile_select', { profile: account.profile }); }, onBack: callback, onFullDraw: function onFullDraw(container) { container.append($('
    ' + Lang.translate('account_profile_info') + '
    ')); } }); } else { Noty.show(result.text); callback && callback(); } })["catch"](function () { Loading$1.stop(); Noty.show(Lang.translate('account_profiles_empty')); }); }, false, true); } var Profile = { init: Utils$1.onceInit(init$W), update: update$8, check: check$2, select: select$2, icon: icon$2 }; function inject(callback) { if (!permit.access) return console.warn('Backup', 'no access'), callback && callback(); confirm$1('', function () { Api$1.load('users/backup/import').then(function (data) { callback && callback(data); if (data.data) { var imp = 0; var ers = 0; for (var i in data.data) { try { localStorage.setItem(i, data.data[i]); imp++; } catch (e) { ers++; } } Noty.show(Lang.translate('account_import_secuses') + ' - ' + Lang.translate('account_imported') + ' (' + imp + '/' + ers + ') - ' + Lang.translate('account_reload_after')); setTimeout(function () { window.location.reload(); }, 5000); } else Noty.show(Lang.translate('nodata')); })["catch"](function () { Noty.show(Lang.translate('account_import_fail')); }); }, callback); } function publish(callback) { if (!permit.access) return console.warn('Backup', 'no access'), callback && callback(); confirm$1('', function () { var file; // Удаляем из бэкапа app.js var serialized = JSON.stringify(localStorage, function (key, value) { return key === 'app.js' ? undefined : value; }); try { file = new File([serialized], "backup.json", { type: "text/plain" }); } catch (e) { console.log('Backup', 'file create error', e.message); } if (!file) { try { file = new Blob([serialized], { type: 'text/plain' }); file.lastModifiedDate = new Date(); } catch (e) { console.log('Backup', 'file create error', e.message); Noty.show(Lang.translate('account_export_fail')); } } if (file) { var formData = new FormData($('
    ')[0]); formData.append("file", file, "backup.json"); $.ajax({ url: Api$1.url() + 'users/backup/export', type: 'POST', data: formData, async: true, cache: false, contentType: false, enctype: 'multipart/form-data', processData: false, headers: { token: permit.token }, success: function success(j) { callback && callback(); if (j.secuses) { if (j.limited) Modal$1.limited();else Noty.show(Lang.translate('account_export_secuses')); } else Noty.show(Lang.translate('account_export_fail')); }, error: function error(e, x) { callback && callback(); console.log('Backup', 'network error', Lampa.Network.errorDecode(e, x)); Noty.show(Lang.translate('account_export_fail_' + (Lampa.Network.errorJSON(e).code || 500))); } }); } else { console.log('Backup', 'file not created'); callback && callback(); } }, callback); } function confirm$1(title, onconfirm, oncancel) { Select.show({ title: Lang.translate(title || 'sure'), nomark: true, items: [{ title: Lang.translate('confirm'), confirm: true, selected: true }, { title: Lang.translate('cancel') }], onSelect: function onSelect(a) { if (a.confirm) { onconfirm && onconfirm(); } else { oncancel && oncancel(); } }, onBack: function onBack() { oncancel && oncancel(); } }); } function select$1(callback) { if (permit.access) { Select.show({ title: Lang.translate('settings_cub_backup'), nomark: true, items: [{ title: Lang.translate('settings_cub_backup_export'), subtitle: Lang.translate('settings_cub_backup_export_descr'), "export": true, selected: true }, { title: Lang.translate('settings_cub_backup_import'), subtitle: Lang.translate('settings_cub_backup_import_descr'), "import": true }, { title: Lang.translate('cancel') }], onSelect: function onSelect(a) { if (a["export"]) { publish(callback); } else if (a["import"]) { inject(callback); } else { callback && callback(); } }, onBack: function onBack() { callback && callback(); } }); } else console.warn('Backup', 'no access'); } var Backup = { select: select$1, publish: publish, inject: inject }; function init$V() { Settings.listener.follow('open', function (e) { if (e.name == 'account') render$a(e.body); }); } function render$a(body) { var account = permit.account; var signed = Boolean(permit.token); var premium = Utils$1.countDays(Date.now(), permit.user.premium); if (!window.lampa_settings.account_sync) { body.find('[data-name="account_use"]').remove(); } Utils$1.qrcode('https://' + object$2.cub_site, body.find('.ad-server__qr')); body.find('.settings--account-signin').toggleClass('hide', signed); body.find('.settings--account-user').toggleClass('hide', !signed); body.find('.settings-param__label').toggleClass('hide', !Boolean(premium)); if (signed) { body.find('.settings--account-user-info .settings-param__value').text(account.email); body.find('.settings--account-user-profile .settings-param__value').text(account.profile.name); body.find('.settings--account-user-out').on('hover:enter', function () { Storage.set('account', ''); Storage.set('account_user', ''); Storage.set('account_email', ''); Settings.update(); Bookmarks$1.update(); }); body.find('.settings--account-user-sync').on('hover:enter', function (e) { account = permit.account; Select.show({ title: Lang.translate('settings_cub_sync'), items: [{ title: Lang.translate('confirm'), subtitle: Lang.translate('account_sync_to_profile') + ' (' + account.profile.name + ')', confirm: true }, { title: Lang.translate('cancel') }], onSelect: function onSelect(a) { if (a.confirm) { var loader = $('
    '); $(e.target).append(loader); Bookmarks$1.sync(function () { loader.remove(); }); } Controller.toggle('settings_component'); }, onBack: function onBack() { Controller.toggle('settings_component'); } }); }); body.find('.settings--account-user-backup').on('hover:enter', function (e) { var loader = $('
    '); $(e.target).append(loader); Backup.select(function () { loader.remove(); Controller.toggle('settings_component'); }); }); body.find('.settings--account-user-profile .settings-param__value').text(account.profile.name); body.find('.settings--account-user-profile').on('hover:enter', function () { Profile.select(function () { Controller.toggle('settings_component'); }); }); } } var Panel = { init: Utils$1.onceInit(init$V), render: render$a }; var network$6 = new Request(); var user_data; /** * Запуск */ function init$U() { if (!window.lampa_settings.account_use) return; console.log('Account', 'use', permit.token ? permit.account.email : 'no account'); Timeline$2.init(); Bookmarks$1.init(); Profile.init(); Panel.init(); Device.init(); Socket.listener.follow('open', checkAccountValidity); Timer.add(1000 * 60 * 10, checkAccountValidity); Profile.check(function () { Api$1.user(function (user) { return user_data = user; }); Timeline$2.update(); Profile.update(); Api$1.persons(); }); delete Account.init; } /** * Задача загрузки приложения * @param {Function} call - функция обратного вызова * @return {void} */ function task$3(call) { if (!window.lampa_settings.account_use) return call(); Bookmarks$1.update(call); } /** * Проверить валидность аккаунта * @return {void} */ function checkAccountValidity() { permit.token && Socket.send('check_token', {}); } /** * Выход из аккаунта * @param {Object} data - данные аккаунта * @return {void} */ function logoff(data) { var account = permit.account; if (account.token && account.email == data.email) { Storage.set('account', '', true); Storage.set('account_use', false, true); Storage.set('account_user', '', true); Storage.set('account_email', '', true); Storage.set('account_notice', '', true); Storage.set('account_bookmarks', '', true); $('.head .open--profile').addClass('hide'); window.location.reload(); } } var Account = { Api: Api$1, Bookmarks: Bookmarks$1, Profile: Profile, Timeline: Timeline$2, Permit: permit, Modal: Modal$1, listener: Listener$1, network: network$6, init: init$U, task: task$3, working: function working() { console.warn('Account.working() is deprecated, use Account.Permit.sync'); return permit.sync; }, canSync: function canSync() { console.warn('Account.canSync() is deprecated, use Account.Permit.sync ? Account.Permit.account : false'); return permit.sync ? permit.account : false; }, workingAccount: function workingAccount() { console.warn('Account.workingAccount() is deprecated, use Account.Permit.sync ? Account.Permit.account : false'); return permit.sync ? permit.account : false; }, logged: function logged() { console.warn('Account.logged() is deprecated, use Permit.access'); return permit.access; }, get: function get(params) { console.warn('Account.get() is deprecated, use Account.Bookmarks.get()'); return Bookmarks$1.get(params); }, all: function all() { console.warn('Account.all() is deprecated, use Account.Bookmarks.all()'); return Bookmarks$1.all(); }, plugins: function plugins(call) { console.warn('Account.plugins() is deprecated, use Account.Api.plugins()'); Api$1.plugins(call); }, notice: function notice(call) { console.warn('Account.notice() is deprecated, use Account.Api.notices()'); Api$1.notices(call); }, pluginsStatus: function pluginsStatus(plugin, status) { console.warn('Account.pluginsStatus() is deprecated, use Account.Api.pluginToggle()'); Api$1.pluginToggle(plugin, status); }, showProfiles: function showProfiles(callback) { console.warn('Account.showProfiles() is deprecated, use Account.Profile.select()'); Profile.select(callback); }, clear: function clear(where) { console.warn('Account.clear() is deprecated, use Account.Bookmarks.clear()'); Bookmarks$1.clear(where); }, update: function update(call) { console.warn('Account.update() is deprecated, use Account.Bookmarks.update()'); Bookmarks$1.update(call); }, backup: function backup() { console.warn('Account.backup() is deprecated'); }, subscribeToTranslation: function subscribeToTranslation(params, call, error) { console.warn('Account.subscribeToTranslation() is deprecated, use Account.Api.subscribeToTranslation()'); Api$1.subscribeToTranslation(params, call, error); }, subscribes: function subscribes(params, secuses, error) { console.warn('Account.subscribes() is deprecated, use Account.Api.subscribes()'); Api$1.subscribes(params, secuses, error); }, showNoAccount: function showNoAccount() { console.warn('Account.showNoAccount() is deprecated, use Account.Modal.account()'); Modal$1.account(); }, showCubPremium: function showCubPremium() { console.warn('Account.showCubPremium() is deprecated, use Account.Modal.premium()'); Modal$1.premium(); }, showLimitedAccount: function showLimitedAccount() { console.warn('Account.showLimitedAccount() is deprecated, use Account.Modal.limited()'); Modal$1.limited(); }, logoff: logoff, persons: function persons() { console.warn('Account.persons() is deprecated, use Account.Api.persons()'); Api$1.persons(); }, updateUser: function updateUser() { Api$1.user(function (user) { return user_data = user; }); } }; Object.defineProperty(Account, 'hasPremium', { value: function value() { var user = user_data || Storage.get('account_user', '{}'); if (Storage.get('developer_nopremium', 'false')) return 0; return user.id ? Utils$1.countDays(Date.now(), user.premium) : 0; }, writable: false }); var Account$1 = Account; var listener$a = Subscribe$2(), viewed$1; /** * Инициализация * @returns {void} */ function init$T() { read(); } /** * Прочитать прогресс просмотра из localStorage * @returns {void} */ function read() { var no_nolisten = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; viewed$1 = Storage.get(filename(), {}); listener$a.send('read', { data: viewed$1 }); if (no_nolisten) return; Lampa.Listener.send('state:changed', { target: 'timeline', reason: 'read', viewed: viewed$1 }); } /** * Имя файла для хранения прогресса просмотра в localStorage * @returns {string} - имя файла */ function filename() { return 'file_view' + (Account$1.Permit.sync ? '_' + Account$1.Permit.account.profile.id : ''); } /** * Обновить прогресс просмотра * @param {object} params - параметры прогресса * @param {number} params.hash - хеш файла * @param {number} params.percent - процент просмотра (0-100) * @param {number} [params.time] - текущее время просмотра в секундах * @param {number} [params.duration] - общая длительность файла в секундах * @param {number} [params.profile] - ID профиля * @param {number} [params.updated] - когда отметка изменилась, в миллисекундах. Для локальных изменений ставится сам * @param {boolean} [params.received] - флаг, что данные получены с сервера * @returns {void} */ function update$7(params) { if (params.hash == 0) return; // Штампуем только свои изменения: у полученных с сервера или с другого устройства // уже есть штамп автора, и перезаписывать его локальным временем нельзя. // Пишем в params, а не только в road, потому что ниже в сокет уходит именно params. if (!params.received) params.updated = Date.now(); var road = viewed$1[params.hash]; if (typeof road == 'undefined' || typeof road == 'number') { road = { duration: 0, time: 0, percent: 0, profile: 0, updated: 0 }; viewed$1[params.hash] = road; } road.percent = params.percent; if (typeof params.time !== 'undefined') road.time = params.time; if (typeof params.duration !== 'undefined') road.duration = params.duration; if (typeof params.profile !== 'undefined') road.profile = params.profile; if (typeof params.updated !== 'undefined') road.updated = params.updated; // округляем время и длительность до целых секунд road.time = Math.round(road.time); road.duration = Math.round(road.duration); Storage.set(filename(), viewed$1); var layers = [].concat(Activity.renderLayers()); layers.push($(document)); layers.forEach(function (layer) { var line = $('.time-line[data-hash="' + params.hash + '"]', layer).toggleClass('hide', params.percent ? false : true); $('> div', line).css({ width: params.percent + '%' }); $('.time-line-details[data-hash="' + params.hash + '"]', layer).each(function () { var f = format(road); $(this).find('[a="t"]').text(f.time); $(this).find('[a="p"]').text(f.percent); $(this).find('[a="d"]').text(f.duration); $(this).toggleClass('hide', road.duration ? false : true); }); }); listener$a.send('update', { data: { hash: params.hash, road: road } }); Lampa.Listener.send('state:changed', { target: 'timeline', reason: 'update', data: { hash: params.hash, road: road } }); if (!params.received && Account$1.hasPremium()) Socket.send('timeline', { params: params }); } /** * Получить прогресс просмотра * @param {string} hash - хеш файла * @return {object} - объект с прогрессом просмотра {hash, percent, time, duration, profile, updated, handler} */ function view$1(hash) { var curent = typeof viewed$1[hash] !== 'undefined' ? viewed$1[hash] : 0, profile = Account$1.Permit.sync ? Account$1.Permit.account.profile.id : 0; var road = { percent: 0, time: 0, duration: 0, profile: 0, updated: 0 }; if (_typeof(curent) == 'object') { road.percent = curent.percent; road.time = curent.time; road.duration = curent.duration; road.profile = curent.profile || profile; road.updated = curent.updated || 0; } else { road.percent = curent || 0; road.profile = profile; } listener$a.send('view', { data: { hash: hash, road: road } }); return { hash: hash, percent: road.percent, time: road.time, duration: road.duration, profile: road.profile, updated: road.updated, handler: function handler(percent, time, duration) { return update$7({ hash: hash, percent: percent, time: time, duration: duration, profile: road.profile }); } }; } /** * Создать прогресс просмотра * @param {object} params - параметры прогресса от функции view * @return {jQuery} - jQuery объект с прогрессом просмотра */ function render$9(params) { var line = Template.get('timeline', params); line.toggleClass('hide', params.percent ? false : true); return line; } /** * Создать детальную информацию о прогрессе просмотра * @param {object} params - параметры прогресса от функции view * @param {string} [str] - строка для добавления перед прогрессом * @return {jQuery} - jQuery объект с детальной информацией о прогрессе просмотра */ function details(params) { var str = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; var line = Template.get('timeline_details', format(params)); if (str) line.prepend(str); line.attr('data-hash', params.hash); line.toggleClass('hide', params.duration ? false : true); return line; } /** * Проверить, смотрел ли файл * @param {object} card - карточка файла * @return {number} - процент просмотра (0-100) */ function watched(card) { var return_time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (card.original_name) { var max = 24; var list = []; for (var i = 1; i <= max; i++) { var time = view$1(Utils$1.hash([1, i, card.original_name].join(''))); if (time.percent) { list.push({ ep: i, view: time }); } } return return_time ? list : list.length; } else { var _time = view$1(Utils$1.hash(card.original_title)); return return_time ? _time : _time.percent; } } function watchedEpisode(card, season, episode) { var return_time = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var time = view$1(Utils$1.hash([season, season > 10 ? ':' : '', episode, card.original_name || card.original_title].join(''))); return return_time ? time : time.percent; } /** * Форматировать прогресс в понятный человекy вид * @param {object} params - параметры прогресса от функции view * @return {object} - объект с отформатированными параметрами {percent, time, duration} */ function format(params) { var road = { percent: params.percent + '%', time: Utils$1.secondsToTimeHuman(params.time), duration: Utils$1.secondsToTimeHuman(params.duration) }; return road; } var Timeline$1 = { init: Utils$1.onceInit(init$T), read: read, listener: listener$a, render: render$9, update: update$7, view: view$1, details: details, format: format, watched: watched, watchedEpisode: watchedEpisode, filename: filename }; var markers = ['socket', 'mirrors', 'request']; var markers_object = {}; var update_started = false; markers.forEach(function (marker) { markers_object[marker] = { error: false, live: false, pass_count: 0, pass_time: 0, status_now: '', status_prev: '' }; }); function init$S() { markers.forEach(function (marker) { markers_object[marker].element = Head.render()[0].querySelector('.head__markers .item--' + marker); }); if (typeof requestAnimationFrame !== 'undefined') { Lampa.Listener.follow('request_error', function (e) { if (e.params.url.indexOf(object$2.cub_domain) > -1 && e.error.status == 0 && e.exception !== 'timeout') { error$1('request'); } }); Lampa.Listener.follow('request_secuses', function (e) { if (e.params.url.indexOf(object$2.cub_domain) > -1) { pass('request'); normal('request'); } }); } } function update$6() { if (update_started) return; if (typeof requestAnimationFrame !== 'undefined') { update_started = true; requestAnimationFrame(function updateMarkers() { var any = false; markers.forEach(function (marker) { var marker_data = markers_object[marker]; var status = marker_data.pass_count > 0 ? 'status--pass' : marker_data.live ? 'status--live' : marker_data.bad ? 'status--bad' : marker_data.error ? 'status--error' : ''; if (status !== marker_data.status_now) { marker_data.status_prev = marker_data.status_now; marker_data.status_now = status; updateStatus(marker); } if (status == 'status--pass' && marker_data.pass_time < Date.now() - 100) { marker_data.pass_count--; marker_data.pass_time = Date.now(); } if (marker_data.pass_count >= 0) any = true; if (marker_data.pass_count == 0) marker_data.pass_count = -1; }); if (any) requestAnimationFrame(updateMarkers);else update_started = false; }); } } function updateStatus(marker) { var marker_data = markers_object[marker]; if (!marker_data.element) return; var status = marker_data.pass_count > 0 ? 'status--pass' : marker_data.live ? 'status--live' : marker_data.bad ? 'status--bad' : marker_data.error ? 'status--error' : ''; marker_data.element.classList.remove('status--error', 'status--live', 'status--pass', 'status--bad'); if (status) marker_data.element.classList.add(status); } function bad(who) { markers_object[who].error = false; markers_object[who].live = false; markers_object[who].bad = true; update$6(); } function error$1(who) { markers_object[who].error = true; markers_object[who].live = false; markers_object[who].bad = false; update$6(); } function pass(who) { var add = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; markers_object[who].pass_count = Math.min(markers_object[who].pass_count + add, 20); update$6(); } function live(who) { markers_object[who].error = false; markers_object[who].live = true; markers_object[who].bad = false; update$6(); } function normal(who) { markers_object[who].error = false; markers_object[who].live = false; markers_object[who].bad = false; update$6(); } var Markers = { init: init$S, bad: bad, error: error$1, pass: pass, live: live, normal: normal }; var socket; var ping; var _uid = Utils$1.uid(); var _devices = []; var listener$9 = Subscribe$2(); var expects = []; var timeping = 5000; var timeout$1; var used_mirrors = -1; var terminal_access = false; function connect$1() { if (!window.lampa_settings.socket_use) return; var ws = Platform.is('orsay') || Platform.is('netcast') ? 'ws://' : 'wss://'; var pt = Platform.is('orsay') || Platform.is('netcast') ? ':8080' : ':8443'; var mirrors = object$2.soc_mirrors; var mirror = mirrors[used_mirrors + 1] || mirrors[0]; used_mirrors = (used_mirrors + 1) % mirrors.length; var socket_url = ws + mirror + pt; if (window.lampa_settings.socket_url) socket_url = window.lampa_settings.socket_url; clearTimeout(ping); clearTimeout(timeout$1); timeout$1 = setTimeout(function () { console.log('Socket', 'timeout close'); if (socket) socket.close(); }, 1000 * 60); try { socket = new WebSocket(socket_url); } catch (e) { console.log('Socket', 'not work'); } if (!socket) return; socket.addEventListener('open', function (event) { console.log('Socket', 'open on ' + socket_url); timeping = 5000; clearTimeout(timeout$1); send('start', {}); listener$9.send('open', {}); Markers.live('socket'); }); socket.addEventListener('close', function (event) { console.log('Socket', 'close', event.code); clearTimeout(timeout$1); listener$9.send('close', {}); timeping = Math.min(1000 * 60 * 5, timeping); console.log('Socket', 'try connect to ' + socket_url + ' after', Math.round(timeping) / 1000, 'sec.'); setTimeout(connect$1, Math.round(timeping)); timeping *= 2; Markers.error('socket'); }); socket.addEventListener('error', function (event) { console.log('Socket', 'error:', 'maybe there is no connection to the server'); }, false); socket.addEventListener('message', function (event) { if (event.data == 'pong') { socket.alive = true; Markers.live('socket'); return; } var result = JSON.parse(event.data); if (window.lampa_settings.socket_methods) { if (result.method == 'devices') { _devices = result.data; } else if (result.method == 'open') { Controller.toContent(); Activity.push(result.data); } else if (result.method == 'timeline') { //чтоб снова не остправлять и не зациклить result.data.received = true; var account = Account$1.Permit.account; if (Account$1.Permit.sync && account.profile && account.profile.id == result.data.profile) { Timeline$1.update(result.data); } } else if (result.method == 'bookmarks') { Account$1.Bookmarks.update(); } else if (result.method == 'terminal_activate') { if (Storage.get('terminal_access', '') == result.data.code) { terminal_access = true; send('terminal_result', { result: 'Terminal access activated' }); } } else if (result.method == 'terminal_eval') { if (Storage.get('terminal_access', '') == result.data.code) { var stroke = ''; var tojson = {}; console.log('Socket', 'terminal eval', result.data.eval); try { stroke = eval(result.data.eval); } catch (e) { stroke = e.message + ' ' + e.stack; } try { if (Arrays.isObject(stroke) || Arrays.isArray(stroke)) tojson = JSON.stringify(stroke); } catch (e) { tojson = stroke; } if (typeof stroke == 'function') { tojson = 'Function cannot be converted to JSON'; } if (typeof stroke == 'string' || typeof stroke == 'number' || typeof stroke == 'boolean') { tojson = stroke; } else if (stroke === undefined) { tojson = 'undefined'; } else if (stroke === null) { tojson = 'null'; } else tojson = 'unknown type'; console.log('Socket', 'terminal eval result', tojson); send('terminal_result', { result: tojson }); } } else if (result.method == 'logoff') { Account$1.logoff(result.data); } else if (result.method == 'info') { console.log('Socket', 'info', result.data); } else if (result.method == 'token_status') { console.log('Socket', 'token status', result.data); } else if (result.method == 'buy_premium') { Account$1.updateUser(); } else if (result.method == 'other' && result.data.submethod == 'play') { Controller.toContent(); Modal.open({ title: '', align: 'center', html: $('
    ' + Lang.translate('confirm_open_player') + '
    '), buttons: [{ name: Lampa.Lang.translate('settings_param_no'), onSelect: function onSelect() { Modal.close(); Controller.toggle('content'); } }, { name: Lampa.Lang.translate('settings_param_yes'), onSelect: function onSelect() { Modal.close(); Controller.toggle('content'); Player.play(result.data.object.player); Player.playlist(result.data.object.playlist); } }], onBack: function onBack() { Modal.close(); Controller.toggle('content'); } }); } } Markers.pass('socket', 5); listener$9.send('message', result); }); Timer.add(1000, function () { if (expects.length > 50) expects = expects.slice(-50); if (socket && socket.readyState == 1 && expects.length) { var msg = expects.shift(); console.log('Socket', 'sent with a delay:', msg.method); send(msg.method, msg); } }, false, true); Timer.add(1000 * 30, function () { if (socket && socket.readyState == 1) { clearTimeout(ping); socket.alive = false; socket.send('ping'); ping = setTimeout(function () { if (!socket.alive && socket.readyState == 1) { console.log('Socket', 'ping timeout, maybe connection lost'); Markers.bad('socket'); } }, 15000); } }, false, true); } function send(method, data) { var name_devise = Platform.get() ? Platform.get() : navigator.userAgent.toLowerCase().indexOf('mobile') > -1 ? 'mobile' : navigator.userAgent.toLowerCase().indexOf('x11') > -1 ? 'chrome' : 'other'; listener$9.send('send', { method: method, data: data }); data.device_id = _uid; data.name = Utils$1.capitalizeFirstLetter(name_devise) + ' - ' + Storage.field('device_name'); data.method = method; data.version = 2; data.account = Storage.get('account', '{}'); data.premium = Account$1.hasPremium(); data.terminal = Storage.get('terminal_access', ''); if (socket && socket.readyState == 1) socket.send(JSON.stringify(data));else expects.push(data); Markers.pass('socket'); } function restart() { if (socket) socket.close(); connect$1(); } var Socket = { listener: listener$9, init: connect$1, send: send, uid: function uid() { return _uid; }, devices: function devices() { return _devices; }, restart: restart, terminalAccess: function terminalAccess() { return terminal_access; } }; var timer$4; var listener$8; function init$R() { var timer, activity; var broadcast = Head.addIcon(Template.string('icon_broadcast'), function () { open$6({ type: 'card', object: Activity.extractObject(activity) }); }); broadcast.addClass('open--broadcast'); broadcast.hide(); Lampa.Listener.follow('activity', function (e) { if (e.type == 'start') activity = e.object; clearTimeout(timer); timer = setTimeout(function () { if (activity) { if (activity.component !== 'full') { broadcast.hide(); activity = false; } } }, 1000); if (e.type == 'start' && e.component == 'full' && !permit.child) { broadcast.show(); activity = e.object; } }); } /** * Открыть окно * @params {Object} params - параметры * @params {String} params.type - тип (card, play) * @params {Object} params.object - объект * @returns {void} */ function open$6(params) { var enabled = Controller.enabled().name; var text = params.type == 'card' ? Lang.translate('broadcast_open') : params.type == 'play' ? Lang.translate('broadcast_play') : ''; var temp = Template.get('broadcast', { text: text }); var list = temp.find('.broadcast__devices'); var last = ''; if (!text) temp.find('.about').remove(); Socket.send('devices', {}); timer$4 = setInterval(function () { Socket.send('devices', {}); }, 3000); listener$8 = function listener(e) { if (e.method == 'devices') { var devices = e.data.filter(function (d) { return !(d.name == 'CUB' || d.device_id == Socket.uid()); }); var select; list.empty(); devices.forEach(function (device) { var item = $('
    ' + device.name + '
    '); item.on('hover:enter', function () { close$7(); Controller.toggle(enabled); if (params.type == 'card') { var object = Arrays.clone(params.object); var card = { id: object.card.id, source: object.card.source || 'tmdb' }; object.card = card; Socket.send('open', { params: object, uid: device.uid }); } if (params.type == 'play') { var player = Arrays.clone(params.object.player); delete player.playlist; Socket.send('other', { params: { submethod: 'play', object: { player: player, playlist: [] } }, uid: device.uid }); } }).on('hover:focus', function () { last = device.uid; }); list.append(item); if (last == device.uid) select = item[0]; }); Modal.toggle(select); } }; Modal.open({ title: '', html: temp, size: 'small', mask: true, onBack: function onBack() { close$7(); Controller.toggle(enabled); } }); listener$8({ method: 'devices', data: Socket.devices() }); Socket.listener.follow('message', listener$8); } /** * Закрыть окно */ function close$7() { Socket.listener.remove('message', listener$8); clearInterval(timer$4); Modal.close(); listener$8 = null; } var Broadcast = { init: init$R, open: open$6 }; var wait_for_disclaimer = false; var show_disclaimer = false; function init$Q() { Player.listener.follow('destroy', reset); } function needs(player_need, launch_player) { return (Storage.field(player_need) == 'inner' || launch_player == 'inner') && Platform.is('apple_tv') && !show_disclaimer; } function show$6(call) { wait_for_disclaimer = true; show_disclaimer = true; function openPlayerSettingSidebar() { var openPlayer = function openPlayer(event) { if (event.name !== 'player') return; Settings.listener.remove('open', openPlayer); if (!Controller.enabled() || Controller.enabled().name !== 'settings_component') { Controller.toggle('settings_component'); } var player_field = event.body.find('[data-name="player"]'); if (player_field.length) player_field.trigger('hover:enter'); }; Settings.listener.follow('open', openPlayer); Controller.toggle('settings'); Settings.create('player'); } Modal.open({ title: Lang.translate('inner_player_disclaimer_title'), size: 'small', scroll: { nopadding: true }, html: $('
    ' + Lang.translate('inner_player_disclaimer_text') + '
    '), buttons: [{ name: Lang.translate('confirm'), onSelect: function onSelect() { wait_for_disclaimer = false; Modal.close(); call(); } }, { name: Lang.translate('inner_player_disclaimer_change_player'), onSelect: function onSelect() { wait_for_disclaimer = false; Modal.close(); openPlayerSettingSidebar(); } }], onBack: function onBack() { wait_for_disclaimer = false; Modal.close(); } }); } function reset() { wait_for_disclaimer = false; } function isWaiting() { return wait_for_disclaimer; } var Disclaimer = { init: init$Q, needs: needs, show: show$6, reset: reset, isWaiting: isWaiting }; var status = false; function init$P() { $.ajax({ url: "./personal.lampa", dataType: 'text', success: function success() { status = true; } }); } function confirm() { return status; } var Personal = { init: init$P, confirm: confirm }; function init$O() { var today = new Date(); var date = today.toISOString().split('T')[0]; if (Storage.get('metric_date') !== date) { if (Storage.get('metric_adview', 0) > 0) histogram('adview', Storage.get('metric_adview', 0)); Storage.set('metric_date', date); Storage.set('metric_adview', 0); } if (!Storage.get('metric_uid', '')) Storage.set('metric_uid', Utils$1.uid()); $.ajax({ dataType: 'json', url: Utils$1.protocol() + object$2.cub_domain + '/api/metric/unic?platform=' + Platform.get() + '&uid=' + Storage.get('metric_uid', '') + '&premium=' + (Account$1.hasPremium() ? 1 : 0) + '&geo=' + vpn.code() }); counter('screen', Platform.get(), Platform.screen('tv') ? 'tv' : 'mobile'); counter('client_region', vpn.code()); function playerStat(data) { if (!data.iptv) { counter('player_start', Platform.get(), data.stat_from, data.torrent_hash ? 'torrent' : data.youtube ? 'youtube' : data.continue_play ? 'continue' : 'online'); if (data.vast_url) { counter('player_vast_url', Storage.get('activity', '{}').component || 'none', data.continue_play ? 'continue' : 'start'); } } } Lampa.Player.listener.follow('start', function (data) { playerStat(_objectSpread2(_objectSpread2({}, data), {}, { stat_from: 'inner' })); }); Lampa.Player.listener.follow('external', function (data) { playerStat(_objectSpread2(_objectSpread2({}, data), {}, { stat_from: 'external' })); }); } function counter(method, v1, v2, v3) { $.ajax({ dataType: 'json', url: Utils$1.protocol() + object$2.cub_domain + '/api/metric/stat?method=' + method + '&value_one=' + (v1 || '') + '&value_two=' + (v2 || '') + '&value_three=' + (v3 || '') }); } function histogram(method, value) { $.ajax({ dataType: 'json', url: Utils$1.protocol() + object$2.cub_domain + '/api/metric/histogram?method=' + method + '&value=' + (value || 0) }); } var ServiceMetric = { init: init$O, counter: counter, histogram: histogram }; var sdk2_try = 0; var sdk3_try = 0; function loadSDK(api) { return api == 3 ? loadSDK3() : loadSDK2(); } function loadSDK2() { return new Promise(function (resolve, reject) { if (window.VASTPlayer) { return resolve(); } if (sdk2_try > 1) { return reject(new Error('VASTPlayer SDK load failed after multiple attempts')); } sdk2_try++; Utils$1.putScriptAsync([object$2.github_lampa + '/vender/vast/vast.js'], false, reject, resolve); }); } function loadSDK3() { return new Promise(function (resolve, reject) { if (window.google && window.google.ima) { return resolve(); } if (sdk3_try > 1) { return reject(new Error('IMA SDK load failed after multiple attempts')); } sdk3_try++; Utils$1.putScriptAsync(['https://imasdk.googleapis.com/js/sdkloader/ima3.js'], false, reject, resolve); }); } function buildUrl(url) { var movie = Storage.get('activity', '{}').movie; var movie_genres = []; var movie_id = movie ? movie.id : 0; var movie_imdb = movie ? movie.imdb_id : ''; var movie_type = movie ? movie.original_name ? 'tv' : 'movie' : 'movie'; try { movie_genres = movie.genres.map(function (g) { return g.id; }); } catch (e) {} var pixel_ratio = window.devicePixelRatio || 1; var u = url.replace('{RANDOM}', Math.round(Date.now() * Math.random())); u = u.replace(/{TIME}/g, Date.now()); u = u.replace(/{WIDTH}/g, Math.round(window.innerWidth * pixel_ratio)); u = u.replace(/{HEIGHT}/g, Math.round(window.innerHeight * pixel_ratio)); u = u.replace(/{PLATFORM}/g, Platform.get()); u = u.replace(/{UID}/g, encodeURIComponent(getUid())); u = u.replace(/{PIXEL}/g, pixel_ratio); u = u.replace(/{GUID}/g, encodeURIComponent(getGuid())); u = u.replace(/{MOVIE_ID}/g, movie_id); u = u.replace(/{MOVIE_GENRES}/g, movie_genres.join(',')); u = u.replace(/{MOVIE_IMDB}/g, movie_imdb); u = u.replace(/{MOVIE_TYPE}/g, movie_type); u = u.replace(/{SCREEN}/g, encodeURIComponent(Platform.screen('tv') ? 'tv' : 'mobile')); return u; } function getGuid() { var guid = Storage.get('vast_device_guid', ''); if (!guid || guid.indexOf('00000000') === 0) { guid = Utils$1.guid(); Storage.set('vast_device_guid', guid); } return guid; } function getUid() { var uid = Storage.get('vast_device_uid', ''); if (!uid) { uid = Utils$1.uid(15); Storage.set('vast_device_uid', uid); } return uid; } function getMediaType(player_data) { var is_torrent = Boolean(player_data.torrent_hash && Torserver.ip() && player_data.url.indexOf(Torserver.ip()) > -1); var is_youtube = Boolean(player_data.youtube && Activity.active().component == 'full' && player_data.url.indexOf('youtube.com') > -1); var is_continue = Boolean(player_data.continue_play && Lampa.PlayerPlaylist.get().length > 0 && Lampa.PlayerPlaylist.get().indexOf(player_data) > -1); return { iptv: player_data.iptv, torrent: is_torrent, youtube: is_youtube, "continue": is_continue, any: is_torrent || is_youtube || is_continue || player_data.iptv }; } function canShow(player_data) { var player_type = getMediaType(player_data); var ignore = window.lampa_settings.developer.ads ? false : Account$1.hasPremium() || Personal.confirm(); return player_type.any ? false : !ignore; } function metric(stat_name, method, ad_name) { if (ad_name == 'plugin') { var activity = Storage.get('activity', '{}'); if (activity.component) { ad_name = activity.component; } ServiceMetric.counter('ad_' + stat_name + '_plugin', ad_name, method, Platform.screen('tv') ? 'tv' : 'mobile'); } else { ServiceMetric.counter('ad_' + stat_name, ad_name, method, Platform.screen('tv') ? 'tv' : 'mobile'); } } var IMA = { loadSDK: loadSDK, loadSDK2: loadSDK2, loadSDK3: loadSDK3, buildUrl: buildUrl, getGuid: getGuid, getUid: getUid, getMediaType: getMediaType, canShow: canShow, metric: metric }; var last_responce = {}; function stat$2(method, name) { IMA.metric('new_preroll', method, name); } window.adv_logs_responce_event = function (e) { last_responce = { status: e.status, text: e.text }; console.log('Ad', 'logs responce', last_responce); }; /** * Извлекает skipoffset из VAST XML и возвращает в секундах * @param {string} vastXml - XML строка VAST * @param {number} [duration] - продолжительность рекламы в секундах (нужно, если skipoffset в процентах) * @returns {number|null} - время пропуска в секундах или null */ function getSkipOffsetSeconds(vastXml) { var duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; if (!vastXml) return null; // Находим атрибут skipoffset="..." var match = vastXml.match(/skipoffset\s*=\s*["']([^"']+)["']/i); if (!match) return null; var value = match[1].trim(); // Если формат HH:MM:SS if (/^\d{2}:\d{2}:\d{2}$/.test(value)) { var _value$split$map = value.split(':').map(Number), _value$split$map2 = _slicedToArray(_value$split$map, 3), h = _value$split$map2[0], m = _value$split$map2[1], s = _value$split$map2[2]; return h * 3600 + m * 60 + s; } // Если формат процентов, например "25%" if (/^\d+%$/.test(value)) { var percent = parseInt(value); if (duration > 0) { return Math.round(duration * percent / 100); } return null; // нельзя посчитать без duration } // Неизвестный формат return null; } var Vast = /*#__PURE__*/function () { function Vast(preroll) { _classCallCheck(this, Vast); this.listener = Subscribe$2(); this.paused = false; this.preroll = preroll; this.elems = {}; this.tiks = {}; this.skip_time = 15; this.skip_ready = false; this.timewait = 10 * 1000; this.created_at = Date.now(); setTimeout(this.start.bind(this), 100); } /** * Запустить рекламу */ _createClass(Vast, [{ key: "start", value: function start() { Storage.set('metric_adview', Storage.get('metric_adview', '0') + 1); stat$2('launch', this.preroll.name); this.elems.block = Template.js('ad_video_block'); this.elems.skip = this.elems.block.find('.ad-video-block__skip'); this.elems.progressbar = this.elems.block.find('.ad-video-block__progress-fill'); this.elems.loader = this.elems.block.find('.ad-video-block__loader'); this.elems.container = this.elems.block.find('.ad-video-block__vast'); this.elems.status = Template.elem('div', { "class": 'ad-video-block__status' }); this.elems.block.find('video').remove(); this.elems.block.append(this.elems.status); this.elems.container.style.opacity = 0; this.elems.status.style.position = 'absolute'; this.elems.status.style.top = '1em'; this.elems.status.style.left = '1em'; this.elems.status.style.textShadow = '0 0 5px black'; this.elems.status.style.fontSize = '0.9em'; this.elems.block.find('.ad-video-block__text').text(Lang.translate('ad') + ' - ' + Lang.translate('ad_disable')).toggleClass('hide', Boolean(this.preroll.msg)); this.elems.block.find('.ad-video-block__info').text(''); if (this.preroll.msg) this.elems.block.find('.ad-video-block__text').text(this.preroll.msg + ' - ' + Lang.translate('ad_disable')).toggleClass('hide', false); this.elems.block.on('click', this.skip.bind(this)); document.body.append(this.elems.block); this.listener.send('launch'); this.controller(); this.timeout(); console.log('Ad', 'run', this.preroll.name, 'from', this.preroll.name == 'plugin' ? 'plugin' : 'cub'); this.elems.status.text('Initialize...'); try { this.initialize(); stat$2('run', this.preroll.name); } catch (e) { this.error(400, 'Initialize', e ? e.message : ''); } } /** * Контроллер рекламы */ }, { key: "controller", value: function controller() { Controller.add('ad_video_block', { toggle: function toggle() { Controller.clear(); }, enter: this.skip.bind(this), back: this.skip.bind(this) }); Controller.toggle('ad_video_block'); } /** * Ждем загрузки определенное время * после выдаем ошибку */ }, { key: "timeout", value: function timeout() { var _this = this; this.tiks.timeout = setTimeout(function () { _this.error(300, 'Timeout'); }, this.timewait); } /** * Инициализация плеера */ }, { key: "initialize", value: function initialize() { var _this2 = this; this.player = new VASTPlayer(this.elems.container); this.player.load(this.url()).then(function () { _this2.elems.status.text('Loaded'); if (_this2.removed) return _this2.player.stopAd();else { _this2.listeners(); return _this2.player.startAd(); } })["catch"](function (reason) { _this2.elems.status.text('Load error'); if (!_this2.removed) _this2.error(100, reason.message); }); } /** * Слушатели плеера */ }, { key: "listeners", value: function listeners() { var _this3 = this; this.player.on('AdPaused', function () { console.log('Ad', 'event', 'pause'); _this3.paused = true; }); this.player.on('AdPlaying', function () { console.log('Ad', 'event', 'play'); _this3.paused = false; }); this.player.on('AdVideoStart', function () { console.log('Ad', 'event', 'video start'); var video = _this3.player.container.find('video'); if (video) { video.addEventListener('pause', function () { if (_this3.removed) return; console.log('Ad', 'event', 'pause'); _this3.paused = true; }); } }); this.player.once('AdStarted', this.onStarted.bind(this)); this.player.once('AdStopped', this.onStoped.bind(this)); } /** * Реклама запущена */ }, { key: "onStarted", value: function onStarted() { var _this4 = this; console.log('Ad', 'event', 'started'); this.elems.status.text('Started: ' + (this.player.adDuration ? Math.round(this.player.adDuration) + 's' : '')); stat$2('started', this.preroll.name); clearTimeout(this.tiks.timeout); this.elems.loader.remove(); this.elems.container.style.opacity = 1; var duration = this.player.adDuration || this.skip_time; clearInterval(this.tiks.progress); clearTimeout(this.tiks.watch); var creative_skip = getSkipOffsetSeconds(last_responce.text, duration); creative_skip = creative_skip !== null ? creative_skip : duration * 0.8; console.log('Ad', 'creative skip offset:', creative_skip); this.started_time = Date.now(); this.skip_time = Math.round(Math.max(this.skip_time, Math.min(60, creative_skip))); console.log('Ad', 'skip time set to:', this.skip_time); this.tiks.progress = setInterval(this.onProgress.bind(this), 100); this.tiks.watch = setTimeout(function () { _this4.elems.status.text('Watch timeout: ' + duration + 's'); console.log('Ad', 'error', 'watch timeout', duration); _this4.stop(); _this4.onEnd(); }, Math.round((duration + 5) * 1000)); this.player.adVolume = 0.5; } /** * Событие окончания рекламы */ }, { key: "onEnd", value: function onEnd() { console.log('Ad', 'complete'); stat$2('complete', this.preroll.name); this.listener.send('ended'); } /** * Реклама закончена */ }, { key: "onStoped", value: function onStoped() { console.log('Ad', 'stoped'); this.destroy(); this.onEnd(); } /** * Обновление прогресса */ }, { key: "onProgress", value: function onProgress() { var duration = this.player.adDuration || this.skip_time; var remaining = duration - (Date.now() - this.started_time) / 1000; var progress = Math.min(100, (1 - remaining / duration) * 100); var elapsed = duration - remaining; this.elems.progressbar.style.width = progress + '%'; this.skip_ready = elapsed > this.skip_time; var user_view = Math.max(0, duration > this.skip_time ? this.skip_time - elapsed : remaining); this.elems.skip.find('span').text(Lang.translate(this.skip_ready ? 'ad_skip' : Math.ceil(user_view))); if (remaining <= 0) clearInterval(this.tiks.progress); } /** * Сформировать URL для запроса рекламы */ }, { key: "url", value: function url() { return IMA.buildUrl(this.preroll.url); } /** * Обработка ошибки */ }, { key: "error", value: function error(code, msg) { console.log('Ad', 'error', code, msg); this.stop(); this.listener.send('error'); stat$2('error', this.preroll.name); stat$2('error_' + code, this.preroll.name); } /** * Пропустить рекламу если можно */ }, { key: "skip", value: function skip() { if (this.removed) return; if (this.skip_ready || (Date.now() - this.created_at) / 1000 > this.skip_time) { this.stop(); this.onEnd(); } } /** * Остановить рекламу принудительно */ }, { key: "stop", value: function stop() { try { this.player._events = {}; this.player.stopAd().then(this.destroy.bind(this))["catch"](this.destroy.bind(this)); } catch (e) { console.error('Ad', 'stop error', e ? e.message : ''); this.destroy(); } } /** * Уничтожить */ }, { key: "destroy", value: function destroy() { if (this.removed) return; if (this.player) this.player._events = {}; clearTimeout(this.tiks.timeout); clearTimeout(this.tiks.watch); clearInterval(this.tiks.progress); this.elems.block.remove(); this.removed = true; } }]); return Vast; }(); function stat$1(method, name) { IMA.metric('new_preroll', method, name); } var Vast3 = /*#__PURE__*/function () { function Vast3(preroll) { _classCallCheck(this, Vast3); this.listener = Subscribe$2(); this.preroll = preroll; this.elems = {}; this.tiks = {}; this.skip_time = 15; this.skip_ready = false; this.timewait = 12 * 1000; this.created_at = Date.now(); this.removed = false; this.adsManager = null; this.adsLoader = null; this.adContainer = null; setTimeout(this.start.bind(this), 100); } /** * Запустить рекламу */ _createClass(Vast3, [{ key: "start", value: function start() { var _this = this; Storage.set('metric_adview', Storage.get('metric_adview', '0') + 1); stat$1('launch', this.preroll.name); this.elems.block = Template.js('ad_video_block'); this.elems.skip = this.elems.block.find('.ad-video-block__skip'); this.elems.skip.style.display = 'none'; this.elems.progressbar = this.elems.block.find('.ad-video-block__progress-fill'); this.elems.loader = this.elems.block.find('.ad-video-block__loader'); this.elems.container = this.elems.block.find('.ad-video-block__vast'); this.elems.status = Template.elem('div', { "class": 'ad-video-block__status' }); // Создаём video-элемент для IMA SDK this.elems.video = Template.elem('video', { playsinline: '', muted: '', style: 'position:absolute;width:100%;height:100%;top:0;left:0;object-fit:contain;' }); this.elems.block.find('video').remove(); this.elems.container.appendChild(this.elems.video); this.elems.block.append(this.elems.status); this.elems.container.style.opacity = 0; this.elems.status.style.position = 'absolute'; this.elems.status.style.top = '1em'; this.elems.status.style.left = '1em'; this.elems.status.style.textShadow = '0 0 5px black'; this.elems.status.style.fontSize = '0.9em'; this.elems.block.find('.ad-video-block__text').text(Lang.translate('ad') + ' - ' + Lang.translate('ad_disable')).toggleClass('hide', Boolean(this.preroll.msg)); this.elems.block.find('.ad-video-block__info').text(''); if (this.preroll.msg) this.elems.block.find('.ad-video-block__text').text(this.preroll.msg + ' - ' + Lang.translate('ad_disable')).toggleClass('hide', false); this.elems.block.on('click', this.skip.bind(this)); document.body.append(this.elems.block); this.listener.send('launch'); this.controller(); this.timeout(); console.log('Ad3', 'run', this.preroll.name, 'from', this.preroll.name == 'plugin' ? 'plugin' : 'cub'); this.elems.status.text('Loading IMA SDK...'); IMA.loadSDK3().then(function () { if (_this.removed) return; _this.elems.status.text('Initialize...'); try { _this.initialize(); stat$1('run', _this.preroll.name); } catch (e) { _this.error(400, 'Initialize', e ? e.message : ''); } })["catch"](function (e) { _this.error(500, 'SDK Load', e ? e.message : ''); }); } /** * Контроллер рекламы */ }, { key: "controller", value: function controller() { Controller.add('ad_video_block', { toggle: function toggle() { Controller.clear(); }, enter: this.skip.bind(this), back: this.skip.bind(this) }); Controller.toggle('ad_video_block'); } /** * Ждём загрузки определённое время, затем выдаём ошибку */ }, { key: "timeout", value: function timeout() { var _this2 = this; this.tiks.timeout = setTimeout(function () { _this2.error(300, 'Timeout'); }, this.timewait); } /** * Инициализация Google IMA SDK (VAST 3.0) */ }, { key: "initialize", value: function initialize() { var ima = window.google.ima; ima.settings.setVpaidMode(ima.ImaSdkSettings.VpaidMode.ENABLED); ima.settings.setLocale('ru'); this.adContainer = this.elems.container; var displayContainer = new ima.AdDisplayContainer(this.adContainer, this.elems.video); displayContainer.initialize(); this.adsLoader = new ima.AdsLoader(displayContainer); this.adsLoader.addEventListener(ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, this.onAdsManagerLoaded.bind(this), false); this.adsLoader.addEventListener(ima.AdErrorEvent.Type.AD_ERROR, this.onAdError.bind(this), false); var request = new ima.AdsRequest(); request.adTagUrl = this.url(); request.linearAdSlotWidth = this.adContainer.offsetWidth || window.innerWidth; request.linearAdSlotHeight = this.adContainer.offsetHeight || window.innerHeight; request.nonLinearAdSlotWidth = this.adContainer.offsetWidth || window.innerWidth; request.nonLinearAdSlotHeight = Math.floor((this.adContainer.offsetHeight || window.innerHeight) / 3); this.adsLoader.requestAds(request); this.elems.status.text('Requesting...'); } /** * AdsManager загружен */ }, { key: "onAdsManagerLoaded", value: function onAdsManagerLoaded(loadedEvent) { var _this3 = this; if (this.removed) return; var ima = window.google.ima; var adsRenderingSettings = new ima.AdsRenderingSettings(); adsRenderingSettings.uiElements = []; // скрываем встроенную кнопку пропуска IMA SDK this.adsManager = loadedEvent.getAdsManager(this.elems.video, adsRenderingSettings); this.adsManager.addEventListener(ima.AdErrorEvent.Type.AD_ERROR, this.onAdError.bind(this), false); this.adsManager.addEventListener(ima.AdEvent.Type.STARTED, this.onStarted.bind(this), false); this.adsManager.addEventListener(ima.AdEvent.Type.COMPLETE, this.onComplete.bind(this), false); this.adsManager.addEventListener(ima.AdEvent.Type.ALL_ADS_COMPLETED, this.onComplete.bind(this), false); this.adsManager.addEventListener(ima.AdEvent.Type.SKIPPED, this.onComplete.bind(this), false); this.adsManager.addEventListener(ima.AdEvent.Type.AD_PROGRESS, this.onAdProgress.bind(this), false); this.adsManager.addEventListener(ima.AdEvent.Type.PAUSED, function () { _this3.paused = true; }, false); this.adsManager.addEventListener(ima.AdEvent.Type.RESUMED, function () { _this3.paused = false; }, false); this.elems.status.text('Loaded'); try { this.adsManager.init(this.adContainer.offsetWidth || window.innerWidth, this.adContainer.offsetHeight || window.innerHeight, ima.ViewMode.NORMAL); this.adsManager.setVolume(0.5); this.adsManager.start(); } catch (e) { this.error(401, 'AdsManager init', e ? e.message : ''); } } /** * Ошибка IMA SDK */ }, { key: "onAdError", value: function onAdError(adErrorEvent) { var msg = adErrorEvent && adErrorEvent.getError ? adErrorEvent.getError().getMessage() : 'Unknown error'; console.log('Ad3', 'ima error', msg); if (!this.removed) this.error(100, msg); } /** * Реклама запущена */ }, { key: "onStarted", value: function onStarted(adEvent) { var _this4 = this; if (this.removed) return; console.log('Ad3', 'event', 'started'); var ad = adEvent.getAd(); var duration = ad ? ad.getDuration() : this.skip_time; this.elems.status.text('Started: ' + (duration ? Math.round(duration) + 's' : '')); stat$1('started', this.preroll.name); clearTimeout(this.tiks.timeout); this.elems.loader.remove(); this.elems.container.style.opacity = 1; var skip_from_ad = ad ? ad.getSkipTimeOffset() : -1; this.skip_time = skip_from_ad > 0 ? Math.round(Math.min(60, skip_from_ad)) : Math.round(Math.max(this.skip_time, Math.min(60, duration * 0.8))); this.started_time = Date.now(); this.ad_duration = duration; console.log('Ad3', 'skip time set to:', this.skip_time); clearInterval(this.tiks.progress); this.tiks.progress = setInterval(this.onProgress.bind(this), 100); clearTimeout(this.tiks.watch); this.tiks.watch = setTimeout(function () { console.log('Ad3', 'error', 'watch timeout', duration); _this4.elems.status.text('Watch timeout: ' + duration + 's'); _this4.stop(); _this4.onEnd(); }, Math.round((duration + 5) * 1000)); } /** * Прогресс из IMA SDK */ }, { key: "onAdProgress", value: function onAdProgress(adEvent) { var data = adEvent.getAdData(); if (data) { var duration = data.duration || this.ad_duration || this.skip_time; var current = data.currentTime || (Date.now() - this.started_time) / 1000; var remaining = duration - current; var progress = Math.min(100, current / duration * 100); this.elems.progressbar.style.width = progress + '%'; this.skip_ready = current > this.skip_time; var user_view = Math.max(0, duration > this.skip_time ? this.skip_time - current : remaining); this.elems.skip.find('span').text(Lang.translate(this.skip_ready ? 'ad_skip' : Math.ceil(user_view))); } } /** * Обновление прогресса по таймеру (резервный) */ }, { key: "onProgress", value: function onProgress() { var duration = this.ad_duration || this.skip_time; var elapsed = (Date.now() - this.started_time) / 1000; var remaining = duration - elapsed; var progress = Math.min(100, elapsed / duration * 100); this.elems.progressbar.style.width = progress + '%'; this.skip_ready = elapsed > this.skip_time; var user_view = Math.max(0, duration > this.skip_time ? this.skip_time - elapsed : remaining); this.elems.skip.find('span').text(Lang.translate(this.skip_ready ? 'ad_skip' : Math.ceil(user_view))); if (remaining <= 0) clearInterval(this.tiks.progress); } /** * Реклама завершена */ }, { key: "onComplete", value: function onComplete() { if (this.removed) return; console.log('Ad3', 'complete event'); this.destroy(); this.onEnd(); } /** * Событие окончания рекламы */ }, { key: "onEnd", value: function onEnd() { console.log('Ad3', 'complete'); stat$1('complete', this.preroll.name); this.listener.send('ended'); } /** * Сформировать URL для запроса рекламы */ }, { key: "url", value: function url() { return IMA.buildUrl(this.preroll.url); } /** * Обработка ошибки */ }, { key: "error", value: function error(code, msg) { console.log('Ad3', 'error', code, msg); this.stop(); this.listener.send('error'); stat$1('error', this.preroll.name); stat$1('error_' + code, this.preroll.name); } /** * Пропустить рекламу если можно */ }, { key: "skip", value: function skip() { if (this.removed) return; if (this.skip_ready || (Date.now() - this.created_at) / 1000 > this.skip_time) { this.stop(); this.onEnd(); } } /** * Остановить рекламу принудительно */ }, { key: "stop", value: function stop() { try { if (this.adsManager) { this.adsManager.stop(); this.adsManager.destroy(); this.adsManager = null; } } catch (e) { console.error('Ad3', 'stop error', e ? e.message : ''); } this.destroy(); } /** * Уничтожить */ }, { key: "destroy", value: function destroy() { if (this.removed) return; clearTimeout(this.tiks.timeout); clearTimeout(this.tiks.watch); clearInterval(this.tiks.progress); if (this.adsLoader) { try { this.adsLoader.destroy(); } catch (e) {} this.adsLoader = null; } this.elems.block.remove(); this.removed = true; } }]); return Vast3; }(); var VastManager = /*#__PURE__*/function () { function VastManager(params) { _classCallCheck(this, VastManager); this.played = { time: 0, prerolls: [] }; this.data_loaded = { ad: [] }; Arrays.extend(params, { api: 'preroll', cooling: 1000 * 60 * 5 }); this.params = params; } _createClass(VastManager, [{ key: "init", value: function init() { this.load(); Timer.add(1000 * 60 * 60, this.load.bind(this)); } }, { key: "load", value: function load() { var pos = 0; var self = this; var request = function request() { var domain = object$2.cub_mirrors[pos]; if (!domain) return; var url = Utils$1.protocol() + domain + '/api/ad/get/' + self.params.api; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.timeout = 10000; xhr.onload = function () { if (xhr.status >= 200 && xhr.status < 300) { try { var data = JSON.parse(xhr.responseText); if (data.ad && Arrays.isArray(data.ad)) { self.data_loaded.ad = data.ad; console.log('Ad', 'manager ' + self.params.api, 'loaded', self.data_loaded.ad.length); } else { console.log('Ad', 'manager ' + self.params.api, 'wrong format from', domain); pos++; request(); } } catch (e) { console.log('Ad', 'manager ' + self.params.api, 'parse error from', domain); pos++; request(); } } else { console.log('Ad', 'manager ' + self.params.api, 'no load from', domain); pos++; request(); } }; xhr.onerror = function () { console.log('Ad', 'manager ' + self.params.api, 'no load from', domain); pos++; request(); }; xhr.ontimeout = function () { console.log('Ad', 'manager ' + self.params.api, 'timeout from', domain); pos++; request(); }; xhr.send(); }; request(); } }, { key: "coolingReady", value: function coolingReady() { return Date.now() - this.played.time > this.params.cooling; } }, { key: "markCooling", value: function markCooling() { this.played.time = Date.now(); } }, { key: "whitoutGenres", value: function whitoutGenres(whitout_genre) { var movie = Storage.get('activity', '{}').movie; try { var genres = whitout_genre.split(',').map(function (g) { return parseInt(g); }); if (genres.length && movie.genres.find(function (g) { return genres.find(function (gg) { return gg == g.id; }); })) { return true; } } catch (e) {} } }, { key: "orderTag", value: function orderTag(tags) { tags.sort(function (a, b) { return b.impressions - a.impressions; }); return tags[0]; } }, { key: "filter", value: function filter(view) { var _this = this; view = view.filter(function (v) { return !_this.played.prerolls.find(function (pr) { return pr == v.name; }); }); if (!window.lampa_settings.developer.ads) { view = view.filter(function (v) { return _this.whitoutGenres(v.whitout_genre) !== true; }); view = view.filter(function (v) { return v.screen == (Platform.screen('tv') ? 'tv' : 'mobile') || v.screen == 'all'; }); view = view.filter(function (v) { return v.platforms.indexOf(Platform.get()) !== -1 || v.platforms.indexOf('all') !== -1 || !v.platforms.length; }); view = view.filter(function (v) { return v.region.split(',').indexOf(vpn.code()) !== -1 || v.region.indexOf('all') !== -1 || !v.region.length; }); } console.log('Ad', 'manager ' + this.params.api, 'filter view', view); if (view.length) { var preroll = this.orderTag(view); this.played.prerolls.push(preroll.name); return preroll; } return null; } }, { key: "get", value: function get() { var first_run = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; if (first_run) this.played.prerolls = []; return this.data_loaded.ad.length ? this.filter(this.data_loaded.ad) : null; } }]); return VastManager; }(); var running = 0; var player_data = {}; var prerolls_played = []; var Manager$1 = new VastManager({ api: 'preroll', cooling: 1000 * 60 * 5 }); function init$N() { Manager$1.init(); } /** * Показать преролл * @param {Object} preroll - данные для показа рекламы * @param {Number} num - номер показа рекламы (для повторов) * @param {Function} started - вызывается при запуске рекламы * @param {Function} ended - вызывается при окончании рекламы * @return {void} */ function video(preroll, num, started, ended) { console.log('Ad', 'preroll launch'); var advert = preroll.vast_api == 3 ? new Vast3(preroll) : new Vast(preroll); var next = function next(mark) { var any = getAnyPreroll(); mark && Manager$1.markCooling(); any ? video(any, num + 1, started, ended) : ended(); }; advert.listener.follow('launch', started); advert.listener.follow('ended', function () { num < 2 ? next() : ended(); }); advert.listener.follow('error', function () { Date.now() - running < 15000 && num < 4 ? next() : ended(); }); } /** * Показать заставку (реклама) * @param {Object} preroll - данные для показа рекламы * @param {Function} call - вызывается при окончании рекламы * @return {void} */ function launch(preroll, call) { var enabled = Controller.enabled().name; Background.theme('#454545'); var html = $("\n
    \n
    \n
    ".concat(Lang.translate('ad'), "
    \n
    \n
    \n ")); $('body').append(html); setTimeout(function () { html.find('.ad-preroll__bg').addClass('animate'); setTimeout(function () { html.find('.ad-preroll__text').addClass('animate'); }, 500); }, 100); setTimeout(function () { html.find('.ad-preroll__over').addClass('animate'); setTimeout(function () { Controller.toggle(enabled); Background.theme('black'); video(preroll, 1, function () {}, function () { html.remove(); Background.theme('reset'); Controller.toggle(enabled); call(); }); }, 300); }, 3500); Controller.add('ad_preroll', { toggle: function toggle() { Controller.clear(); }, enter: function enter() {}, back: function back() {} }); Controller.toggle('ad_preroll'); } /** * Получить данные для плагина * @param {Object} data - данные плеера * @return {Object|Boolean} данные для плагина или false, если не показывать */ function getVastPlugin(data) { var show = true; if (data.vast_region && typeof data.vast_region == 'string' && data.vast_region.split(',').indexOf(data.ad_region) == -1) show = false; if (data.vast_platform && typeof data.vast_platform == 'string' && data.vast_platform.split(',').indexOf(Platform.get()) == -1) show = false; if (data.vast_screen && typeof data.vast_screen == 'string' && data.vast_screen.split(',').indexOf(Platform.screen('tv') ? 'tv' : 'mobile') == -1) show = false; if (data.vast_url && typeof data.vast_url == 'string' && show) return { url: data.vast_url, name: 'plugin', msg: data.vast_msg || Lang.translate('ad_plugin') }; return false; } /** * Получить данные для показа рекламы (преролл или плагин) * @param {Boolean} first_run - первый запуск (для сброса кулинга) * @return {Object|Boolean} данные для показа рекламы или false, если не показывать */ function getAnyPreroll() { var first_run = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var manager = Manager$1.get(first_run); var plugin = getVastPlugin(player_data); var any = Manager$1.coolingReady() ? manager || plugin : false; if (any) { if (prerolls_played.indexOf(any.url) == -1) prerolls_played.push(any.url);else any = false; } return any; } /** * Показать рекламу (преролл или плагин) * @param {Object} data - данные плеера * @param {Function} call - вызывается при окончании рекламы * @return {void} */ function show$5(data, call) { player_data = data; prerolls_played = []; // Пометить регион для таргетинга рекламы player_data.ad_region = vpn.code(); // Не показывать рекламу для iptv/torrent/youtube/continue var type = IMA.getMediaType(data); var whoi = Account$1.hasPremium() ? 'premium' : Personal.confirm() ? 'personal' : 'none'; ServiceMetric.counter('ad_preroll_start', vpn.code(), whoi, type.any ? 'skip' : 'show'); if (type.any) { console.log('Ad', 'preroll skipped, no vast api or iptv/torrent/youtube/continue', type); return call(); } // Бывает что плеер по несколько раз запускается, // проверяем чтобы реклама не запускалась несколько раз подряд if (running) return console.log('Ad', 'preroll skipped, already running'); // Помечаем время запуска рекламы running = Date.now(); var ended = function ended() { running = 0; console.log('Ad', 'preroll ended'); call(); }; // Получаем данные для показа рекламы (преролл или плагин) var preroll = getAnyPreroll(true); var canshow = IMA.canShow(data); ServiceMetric.counter('ad_preroll_show', vpn.code(), preroll ? 'ready' : 'none', canshow ? 'ready' : 'none'); ServiceMetric.counter('ad_preroll_colling', vpn.code(), Manager$1.coolingReady() ? 'ready' : 'cooling'); if (preroll && canshow) { // Загружаем SDK для выбранного преролла, чтобы он был готов к показу IMA.loadSDK(preroll.vast_api)["catch"](function () { console.log('Ad', 'IMA SDK load error', preroll.vast_api); }); launch(preroll, ended); } else ended(); } var Preroll = { init: init$N, show: show$5 }; var _DEFAULT_PLAYER_CONFI; var POLLING_INTERVAL_MS = 5000; var MAX_FAILED_ATTEMPTS = 3; var PLAYER_TYPES = { VLC: 'vlc', MPC_HC: 'mpc-hc', MPC_BE: 'mpc-be', MPC_QT: 'mpc-qt', KMPLAYER: 'kmplayer' }; var DEFAULT_PLAYER_CONFIG = (_DEFAULT_PLAYER_CONFI = {}, _defineProperty(_DEFAULT_PLAYER_CONFI, PLAYER_TYPES.VLC, { port: 3999, password: '123456', fullscreen: true }), _defineProperty(_DEFAULT_PLAYER_CONFI, PLAYER_TYPES.MPC_HC, { port: 3999, password: null, fullscreen: true }), _defineProperty(_DEFAULT_PLAYER_CONFI, PLAYER_TYPES.MPC_BE, { port: 3999, password: null, fullscreen: true }), _defineProperty(_DEFAULT_PLAYER_CONFI, PLAYER_TYPES.MPC_QT, { port: 3999, password: null, fullscreen: true }), _defineProperty(_DEFAULT_PLAYER_CONFI, PLAYER_TYPES.KMPLAYER, { port: 3999, password: null, fullscreen: true }), _DEFAULT_PLAYER_CONFI); var playerCallbacks = {}; var pollingInterval = null; var lastSuccessfulTimecode = null; var currentPlayerProcess = null; var currentHash = null; var failedAttempts = 0; /** * Получение URL API в зависимости от типа плеера */ function getPlayerURL(config) { switch (config.type) { case PLAYER_TYPES.VLC: return "http://localhost:".concat(config.port, "/requests/status.json"); case PLAYER_TYPES.MPC_HC: case PLAYER_TYPES.MPC_BE: case PLAYER_TYPES.MPC_QT: return "http://localhost:".concat(config.port, "/variables.html"); case PLAYER_TYPES.KMPLAYER: return "http://localhost:".concat(config.port, "/status.html"); default: throw new Error("Unknown player type: ".concat(config.type)); } } /** * Парсинг ответа MPC-HC/MPC-BE/MPC-QT из HTML */ function parseMPCResponse(html) { var parser = new DOMParser(); var doc = parser.parseFromString(html, 'text/html'); var getValue = function getValue(id) { var element = doc.getElementById(id); return element ? element.textContent : null; }; var state = getValue('state'); var position = getValue('position'); var duration = getValue('duration'); var statestring = getValue('statestring'); var file = getValue('file'); var filepath = getValue('filepath'); return { state: state ? parseInt(state) : null, statestring: statestring, position: position ? parseInt(position) : null, duration: duration ? parseInt(duration) : null, file: file, filepath: filepath, volumelevel: getValue('volumelevel'), muted: getValue('muted'), playbackrate: getValue('playbackrate') }; } /** * Парсинг ответа KMPlayer */ function parseKMPlayerResponse(text) { var startIndex = text.indexOf('OnStatus('); if (startIndex === -1) return null; var argsStart = startIndex + 9; var lastParen = text.lastIndexOf(')'); if (lastParen === -1) return null; var argsStr = text.substring(argsStart, lastParen); var args = []; var current = ''; var inQuotes = false; for (var i = 0; i < argsStr.length; i++) { var _char = argsStr.charAt(i); if (_char === '"') { inQuotes = !inQuotes; } else if (_char === ',' && !inQuotes) { args.push(current.trim()); current = ''; } else { current += _char; } } args.push(current.trim()); for (var _i = 0; _i < args.length; _i++) { args[_i] = args[_i].trim(); if (args[_i].charAt(0) === '"' && args[_i].charAt(args[_i].length - 1) === '"') { args[_i] = args[_i].substring(1, args[_i].length - 1); } } return { file: args[0] || null, statestring: args[1] || null, position: args[2] ? parseInt(args[2], 10) : null, duration: args[4] ? parseInt(args[4], 10) : null, url: args[8] || null }; } /** * Парсинг ответа VLC из JSON */ function parseVLCResponse(json) { return { time: json.time, length: json.length, state: json.state, stats: json.stats }; } /** * Нормализация данных таймкода из разных плееров */ function normalizeTimecode(playerData, config) { if (config.type === PLAYER_TYPES.VLC) { if (playerData.time != null && playerData.length) { // VLC возвращает секунды return { currentTime: playerData.time, duration: playerData.length, percent: Math.round(playerData.time / playerData.length * 100) }; } } else if (config.type === PLAYER_TYPES.MPC_HC || config.type === PLAYER_TYPES.MPC_BE || config.type === PLAYER_TYPES.MPC_QT || config.type === PLAYER_TYPES.KMPLAYER) { if (playerData.position && playerData.duration) { // MPC-HC, MPC-BE, MPC-QT и KMPlayer возвращают миллисекунды return { currentTime: playerData.position / 1000, duration: playerData.duration / 1000, percent: Math.round(playerData.position / playerData.duration * 100) }; } } return null; } /** * Получение статуса плеера */ function getPlayerStatus(htmlOrJson, config) { if (config.type === PLAYER_TYPES.VLC) { return parseVLCResponse(htmlOrJson); } else if (config.type === PLAYER_TYPES.MPC_HC || config.type === PLAYER_TYPES.MPC_BE || config.type === PLAYER_TYPES.MPC_QT) { return parseMPCResponse(htmlOrJson); } else if (config.type === PLAYER_TYPES.KMPLAYER) { return parseKMPlayerResponse(htmlOrJson); } return null; } /** * Сохранение таймкода при закрытии плеера */ function saveTimecodeOnPlayerClose(hash) { if (lastSuccessfulTimecode && hash && playerCallbacks[hash]) { console.log('Player', 'Процесс закрылся, сохраняем последний timecode:', lastSuccessfulTimecode); playerCallbacks[hash](lastSuccessfulTimecode.percent, lastSuccessfulTimecode.currentTime, lastSuccessfulTimecode.duration); stopTimecodePolling(); delete playerCallbacks[hash]; currentPlayerProcess = null; currentHash = null; } else if (hash && playerCallbacks[hash]) { console.log('Player', 'Процесс закрылся, но timecode не был получен'); stopTimecodePolling(); delete playerCallbacks[hash]; currentPlayerProcess = null; currentHash = null; } } /** * Получение и сохранение статуса из плеера */ function fetchAndSaveTimecode(config, hash) { var fetchOptions = {}; // Добавляем авторизацию только для VLC if (config.type === PLAYER_TYPES.VLC && config.password) { fetchOptions.headers = { 'Authorization': "Basic ".concat(btoa(':' + config.password)) }; } return fetch(getPlayerURL(config), fetchOptions).then(function (response) { if (!response.ok) { throw new Error('Player API вернул ошибку'); } if (config.type === PLAYER_TYPES.VLC) { return response.json(); } else if (config.type === PLAYER_TYPES.MPC_HC || config.type === PLAYER_TYPES.MPC_BE || config.type === PLAYER_TYPES.MPC_QT || config.type === PLAYER_TYPES.KMPLAYER) { return response.text(); } }).then(function (data) { failedAttempts = 0; var playerStatus = getPlayerStatus(data, config); if (playerStatus) { var timecode = normalizeTimecode(playerStatus, config); if (timecode) { lastSuccessfulTimecode = timecode; // console.log('Player', 'Timecode обновлен:', lastSuccessfulTimecode) } } })["catch"](function (error) { console.error('Player', 'Ошибка получения timecode:', error); failedAttempts++; console.log('Player', "\u041D\u0435\u0443\u0434\u0430\u0447\u043D\u044B\u0445 \u043F\u043E\u043F\u044B\u0442\u043E\u043A: ".concat(failedAttempts, "/").concat(MAX_FAILED_ATTEMPTS)); if (!currentPlayerProcess || failedAttempts >= MAX_FAILED_ATTEMPTS) { if (!currentPlayerProcess) { console.log('Player', 'Процесс закрыт, останавливаем polling'); } else { console.log('Player', 'Превышен лимит неудачных попыток, останавливаем polling'); } if (lastSuccessfulTimecode && playerCallbacks[hash]) { console.log('Player', 'Сохраняем последний timecode перед остановкой'); playerCallbacks[hash](lastSuccessfulTimecode.percent, lastSuccessfulTimecode.currentTime, lastSuccessfulTimecode.duration); } stopTimecodePolling(); if (playerCallbacks[hash]) { delete playerCallbacks[hash]; } currentHash = null; } throw error; }); } /** * Запуск периодического опроса timecode из плеера * @param {string} hash - идентификатор сессии * @param {Object} data - данные сессии * @param {Object} config - конфигурация плеера */ function startTimecodePolling(hash, data, config) { stopTimecodePolling(); lastSuccessfulTimecode = null; failedAttempts = 0; console.log('Player', 'Делаем первый запрос timecode'); fetchAndSaveTimecode(config, hash)["catch"](function (error) { console.error('Player', 'Первый запрос не удался'); }); pollingInterval = setInterval(function () { if (!currentPlayerProcess) { console.log('Player', 'Процесс не найден, останавливаем polling'); stopTimecodePolling(); return; } fetchAndSaveTimecode(config, hash)["catch"](function (error) {// Ошибка уже обработана в fetchAndSaveTimecode }); }, POLLING_INTERVAL_MS); } /** * Остановка периодического опроса timecode */ function stopTimecodePolling() { if (pollingInterval) { clearInterval(pollingInterval); pollingInterval = null; lastSuccessfulTimecode = null; failedAttempts = 0; } } /** * Получение аргументов командной строки для разных плееров */ function getPlayerArgs(config, url, data) { var _data$timeline$time, _data$timeline; // const startTime = (data.timeline?.time ?? 0) * 1000 var startTimeSec = (_data$timeline$time = (_data$timeline = data.timeline) === null || _data$timeline === void 0 ? void 0 : _data$timeline.time) !== null && _data$timeline$time !== void 0 ? _data$timeline$time : 0; if (config.type === PLAYER_TYPES.VLC) { var vlcArgs = ['--extraintf=http', '--http-host=localhost', "--http-port=".concat(config.port), "--http-password=".concat(config.password), "--start-time=".concat(startTimeSec), '--play-and-exit', '--no-loop', encodeURI(url)]; if (config.fullscreen !== false) { vlcArgs.push('--fullscreen'); } return vlcArgs; } else if (config.type === PLAYER_TYPES.MPC_HC || config.type === PLAYER_TYPES.MPC_BE || config.type === PLAYER_TYPES.MPC_QT || config.type === PLAYER_TYPES.KMPLAYER) { var mpcArgs = [url, '/webport', config.port.toString(), '/start', Math.floor(startTimeSec * 1000).toString(), '/play', '/close']; if (config.fullscreen !== false) { mpcArgs.push('/fullscreen'); } return mpcArgs; } return []; } /** * Запуск плеера * @param {string} url - URL медиафайла * @param {Object} data - дополнительные данные * @param {Object} options - опции запуска */ function openPlayer$1(url, data) { var _DEFAULT_PLAYER_CONFI2, _DEFAULT_PLAYER_CONFI3, _DEFAULT_PLAYER_CONFI4; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var _options$type = options.type, type = _options$type === void 0 ? PLAYER_TYPES.VLC : _options$type, _options$port = options.port, port = _options$port === void 0 ? (_DEFAULT_PLAYER_CONFI2 = DEFAULT_PLAYER_CONFIG[type]) === null || _DEFAULT_PLAYER_CONFI2 === void 0 ? void 0 : _DEFAULT_PLAYER_CONFI2.port : _options$port, _options$password = options.password, password = _options$password === void 0 ? (_DEFAULT_PLAYER_CONFI3 = DEFAULT_PLAYER_CONFIG[type]) === null || _DEFAULT_PLAYER_CONFI3 === void 0 ? void 0 : _DEFAULT_PLAYER_CONFI3.password : _options$password, _options$fullscreen = options.fullscreen, fullscreen = _options$fullscreen === void 0 ? (_DEFAULT_PLAYER_CONFI4 = DEFAULT_PLAYER_CONFIG[type]) === null || _DEFAULT_PLAYER_CONFI4 === void 0 ? void 0 : _DEFAULT_PLAYER_CONFI4.fullscreen : _options$fullscreen; var file = require('fs'); var config = { type: type, port: port, password: password, fullscreen: fullscreen }; // Сохраняем hash сразу, чтобы он был доступен при закрытии if (data.timeline) { currentHash = data.timeline.hash; playerCallbacks[data.timeline.hash] = data.timeline.handler; } var playerPath = Storage.field('player_nw_path'); var playerArgs = getPlayerArgs(config, url, data); console.log('Player', 'Путь:', playerPath); console.log('Player', 'Аргументы:', playerArgs); if (playerPath && file.existsSync(playerPath)) { var spawn = require('child_process').spawn; if (currentPlayerProcess) { console.log('Player', 'Убиваем предыдущий процесс'); currentPlayerProcess.kill(); currentPlayerProcess = null; } currentPlayerProcess = spawn(playerPath, playerArgs); var handleProcessClose = function handleProcessClose(code, signal) { console.log('Player', 'Процесс закрыт с кодом:', code, 'сигнал:', signal); if (currentHash && playerCallbacks[currentHash]) { saveTimecodeOnPlayerClose(currentHash); } else { // Если нет колбэка, просто очищаем состояние stopTimecodePolling(); currentHash = null; } currentPlayerProcess = null; }; currentPlayerProcess.on('close', handleProcessClose); currentPlayerProcess.on('error', function (error) { console.error('Player', 'Ошибка процесса:', error); if (currentPlayerProcess) { currentPlayerProcess = null; } handleProcessClose(null, 'error'); }); currentPlayerProcess.on('exit', handleProcessClose); } else { Noty.show(Lang.translate('player_not_found') + ': ' + playerPath); return; } setTimeout(function () { if (data.timeline && currentPlayerProcess) { startTimecodePolling(data.timeline.hash, data, config); } }, 2000); } var ExternalPlayer = { openPlayer: openPlayer$1, stopTimecodePolling: stopTimecodePolling, PLAYER_TYPES: PLAYER_TYPES }; var SAVE_PREFIX = 'infuse://x-callback-url/save?'; var PLAY_PREFIX = 'infuse://x-callback-url/play?'; var SEASON_EPISODE_RE = /\[S(\d+):E(\d+)\]/i; var DEFAULTS = { mode: 'play', // save | play | save_and_play seasonOnly: true, maxItems: 40, maxUrlLength: 65536, // false = не добавлять [источник] в filename; true + data.source = показывать [источник] filenameSource: true, // false = без озвучки; true + voice_name/title = показывать в filename filenameVoice: true }; function parsePositiveInt(value) { var parsed = parseInt(value, 10); return !isNaN(parsed) && parsed > 0 ? parsed : null; } function stripText(value) { if (!value) return ''; var text = Utils$1.clearHtmlTags ? Utils$1.clearHtmlTags(String(value)) : String(value).replace(/<[^>]*>/g, ''); return text.replace(/\s+/g, ' ').trim(); } function pad2(value) { var num = parseInt(value, 10) || 0; return num < 10 ? '0' + num : '' + num; } function sanitizeStreamUrl(url) { return String(url || '').replace('&preload', '&play').replace(/\s/g, '%20'); } function normalizePlayData(data) { if (!data) return data; if (typeof data.url === 'string') { data.url = sanitizeStreamUrl(data.url); } if (Array.isArray(data.playlist)) { data.playlist.forEach(function (item) { if (item && typeof item.url === 'string') { item.url = sanitizeStreamUrl(item.url); } }); } return data; } function serializePlaylist(playlist) { if (!Array.isArray(playlist) || !playlist.length) return ''; var safe = playlist.filter(function (item) { return item && !item.separator && typeof item.url === 'string'; }).map(function (item) { var entry = { url: sanitizeStreamUrl(item.url) }; if (item.title) entry.title = stripText(item.title); return entry; }); if (!safe.length) return ''; try { return encodeURIComponent(JSON.stringify(safe)); } catch (e) { return ''; } } function compareStreamUrl(a, b) { if (!a || !b) return false; a = sanitizeStreamUrl(a); b = sanitizeStreamUrl(b); if (a === b) return true; var baseA = a.split('?')[0]; var baseB = b.split('?')[0]; if (baseA !== baseB) return false; var indexA = (a.match(/[?&]index=(\d+)/i) || [])[1]; var indexB = (b.match(/[?&]index=(\d+)/i) || [])[1]; if (indexA != null || indexB != null) return indexA === indexB; return true; } function parseEpisodeMeta(item) { if (!item) return { season: null, episode: null }; var season = parsePositiveInt(item.season != null ? item.season : item.season_number); var episode = parsePositiveInt(item.episode != null ? item.episode : item.episode_number); if ((!season || !episode) && item.subtitle) { var subtitle = stripText(item.subtitle); var episodeMatch = subtitle.match(/(\d+)\s*$/); if (!episode) episode = parsePositiveInt(episodeMatch && episodeMatch[1]); } if ((!season || !episode) && item.title) { var title = String(item.title); var match = title.match(SEASON_EPISODE_RE); if (match) { if (!season) season = parsePositiveInt(match[1]); if (!episode) episode = parsePositiveInt(match[2]); } else { // Lampa online: "S1 / Серия 5" или "S1 / Episode 5 - title" var onlineSeasonMatch = title.match(/\bS(\d{1,2})\s*\/\s*/i); if (!season && onlineSeasonMatch) season = parsePositiveInt(onlineSeasonMatch[1]); if (!episode) { var _episodeMatch = title.match(/(?:серия|episode|ep\.?)\s*(\d{1,3})/i) || title.match(/^(\d+)\s*\//) || title.match(/(?:^|\s)(\d{1,3})\s*[-–]\s/); if (_episodeMatch) episode = parsePositiveInt(_episodeMatch[1]); } } } if ((!season || !episode) && item.url) { var _match = String(item.url).match(/[Ss](\d{1,2})[Ee](\d{1,3})/i); if (_match) { if (season == null) season = parsePositiveInt(_match[1]); if (episode == null) episode = parsePositiveInt(_match[2]); } } return { season: season, episode: episode }; } function getRawPlaylist(data) { if (!data || !Array.isArray(data.playlist)) return []; return data.playlist.filter(function (item) { return item && !item.separator; }); } function enrichPlayItem(data, item) { item = item || {}; var meta = parseEpisodeMeta(item); var patch = {}; if (item.season == null && meta.season != null) patch.season = meta.season;else if (item.season == null && data && data.season != null) patch.season = data.season; if (item.episode == null && meta.episode != null) patch.episode = meta.episode;else if (item.episode == null && data && data.episode != null) patch.episode = data.episode; if (!Object.keys(patch).length) return item; return Object.assign({}, item, patch); } function resolvePlaylist(data) { var playlist = getRawPlaylist(data); if (!playlist.length) { if (data && data.url) return [enrichPlayItem(data, data)]; return []; } return playlist.map(function (item) { return enrichPlayItem(data, item); }); } function findItemIndexInList(items, data) { if (!items.length) return -1; data = data || {}; var currentUrl = data.url ? sanitizeStreamUrl(data.url) : ''; if (currentUrl) { for (var i = 0; i < items.length; i++) { if (compareStreamUrl(items[i].url, currentUrl)) return i; } } var dataMeta = parseEpisodeMeta(data); if (dataMeta.episode != null) { for (var _i = 0; _i < items.length; _i++) { var meta = parseEpisodeMeta(items[_i]); if (dataMeta.episode !== meta.episode) continue; if (dataMeta.season != null && meta.season != null && dataMeta.season !== meta.season) continue; return _i; } } return -1; } function findPlayItem(data, playlist) { playlist = playlist || resolvePlaylist(data); if (!playlist.length) return data; var index = findItemIndexInList(playlist, data); return index >= 0 ? playlist[index] : data; } function normalizeLaunchMode(mode) { if (mode === 'save' || mode === 'play' || mode === 'save_and_play') return mode; return DEFAULTS.mode; } function isTorrentStream(data) { if (!data) return false; if (data.torrent_hash) return true; var url = String(data.url || ''); return /\/stream\/[^?]+\?link=/.test(url); } function resolveInfuseMode(data) { var mode; if (data && data.infuse_mode) { mode = normalizeLaunchMode(data.infuse_mode); } else { var stored = Storage.field('infuse_launch_mode') || DEFAULTS.mode; if (stored === 'ask') mode = normalizeLaunchMode(DEFAULTS.mode);else mode = normalizeLaunchMode(stored); } // Торрент: только play? — save не возвращает position if (isTorrentStream(data) && mode !== 'play') return 'play'; return mode; } function resolveOptions(data) { var callbacks = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; data = data || {}; var season = data.season != null ? parseInt(data.season, 10) : null; if ((season == null || isNaN(season)) && data) { season = parseEpisodeMeta(data).season; } return { mode: resolveInfuseMode(data), seasonOnly: isTorrentStream(data) ? false : data.infuse_season_only !== false, season: season, maxItems: data.infuse_max_items || DEFAULTS.maxItems, maxUrlLength: data.infuse_max_url_length || DEFAULTS.maxUrlLength, source: data.source || '', x_success: callbacks.x_success, x_error: callbacks.x_error }; } function getExtension(item) { if (!item || typeof item.url !== 'string') return '.mkv'; var path = item.url.split('?')[0].split('#')[0].toLowerCase(); var extMatch = /\.([a-z0-9]{2,5})$/i.exec(path); if (extMatch) return '.' + extMatch[1].toLowerCase(); return '.mkv'; } function resolveCard$1(data) { if (data && (data.card || data.movie)) return data.card || data.movie; var activity = Activity.active(); if (activity && (activity.card || activity.movie)) return activity.card || activity.movie; return null; } function getHumanTitle(movie) { if (!movie) return ''; var rawTitle = (movie.original_name || movie.original_title || movie.name || movie.title || '').trim(); return stripText(rawTitle) || ''; } function isSeriesMedia(movie, item, data) { if (movie) { if (movie.media_type === 'tv') return true; if (movie.first_air_date || movie.number_of_seasons || movie.number_of_episodes) return true; if (movie.name && !movie.title && !movie.release_date) return true; } var meta = parseEpisodeMeta(item || data); if (meta.season != null || meta.episode != null) return true; if (data && (data.season != null || data.episode != null)) return true; return false; } function resolveMediaTitle(movie, data, item) { var title = getHumanTitle(movie); if (title) return title; var sources = [data && data.title, item && item.title, data && data.path]; for (var i = 0; i < sources.length; i++) { if (!sources[i]) continue; title = stripText(String(sources[i])).replace(SEASON_EPISODE_RE, '').replace(/\s*\[[^\]]*\]\s*/g, ' ').replace(/\s*\(\d{4}\)\s*/g, ' ').replace(/\s+/g, ' ').trim(); if (title) return title; } return ''; } function resolveMovieYear(movie, data, item) { var year = getMovieYear(movie); if (year) return year; var text = [data && data.title, item && item.title].filter(Boolean).join(' '); var match = text.match(/\((\d{4})\)/); return match ? match[1] : ''; } function formatSourceLabel(raw) { if (!raw) return ''; var name = stripText(raw); if (!name) return ''; var bracketIdx = name.indexOf('['); if (bracketIdx !== -1) name = name.slice(0, bracketIdx).trim(); name = name.replace(/[^\w\u0400-\u04FF.-]+/g, ' ').replace(/\s+/g, ' ').trim(); if (!name) return ''; var first = name.split(/\s+/)[0]; if (first.endsWith("'s") || first.endsWith('s')) { first = first.replace(/['']s$/i, '').replace(/s$/i, ''); } return first || name.split(/\s+/)[0]; } function formatVoiceLabel(item, isSeries, humanTitle) { if (!item) return ''; var voiceName = stripText(item.voice_name); var title = stripText(item.title); if (!voiceName && !isSeries && title && !SEASON_EPISODE_RE.test(title)) { voiceName = title; } if (!voiceName) return ''; if (humanTitle && voiceName.toLowerCase() === humanTitle.toLowerCase()) return ''; return voiceName.replace(/[\\\/:*?"<>|]/g, '').trim(); } function sanitizeFilenamePart(text) { if (!text) return ''; return stripText(text).replace(/[\\\/:*?"<>|\[\]]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, ''); } function appendFilenameTag(base, label) { var part = sanitizeFilenamePart(label); if (!part) return base; return base + ' [' + part + ']'; } function getSubtitleUrl(item, data) { var subs = item && item.subtitles || data && data.subtitles; if (!subs || !Array.isArray(subs) || !subs.length) return ''; for (var i = 0; i < subs.length; i++) { if (subs[i] && subs[i].url) return String(subs[i].url); } return ''; } function getMovieYear(movie) { if (!movie) return ''; var year = String(movie.release_date || movie.first_air_date || '').slice(0, 4); return year && year !== '0000' ? year : ''; } function getTorrentFileMeta(item) { var segment = ''; if (item && item.path) { segment = String(item.path).split('/').pop(); } else if (item && item.url) { segment = String(item.url).split('?')[0].split('/').pop() || ''; try { segment = decodeURIComponent(segment); } catch (e) {} } if (!segment) { return { name: 'file', extension: getExtension(item) }; } var dotIndex = segment.lastIndexOf('.'); if (dotIndex <= 0) { return { name: segment, extension: getExtension(item) }; } return { name: segment.slice(0, dotIndex), extension: segment.slice(dotIndex) }; } function generateTorrentFilename(item, movie, data, launchMode) { data = data || item || {}; item = item || {}; var fileMeta = getTorrentFileMeta(item); var isSeries = isSeriesMedia(movie, item, data); var name = fileMeta.name.replace(/\s*\{tmdb-\d+\}\s*/gi, '').trim(); var ext = fileMeta.extension || getExtension(item); if (!name) name = 'file'; if (!isSeries && movie && movie.id && name.indexOf('{tmdb-') === -1) { name += ' {tmdb-' + movie.id + '}'; } return name + ext; } function generateFilename(item, movie, sourceOverride, data, launchMode) { data = data || item || {}; var extension = getExtension(item); var meta = parseEpisodeMeta(item); var season = meta.season != null ? meta.season : data.season; var episode = meta.episode != null ? meta.episode : data.episode; if (isTorrentStream(data) && episode == null && item && item.path) { var match = item.path.split('/').pop().replace(/\.[^.]+$/, '').match(/[Ss][\s._-]?(\d+)[\s._-]?[Ee][\s._-]?(\d+)/i); if (match) { season = parseInt(match[1], 10) || season || 1; episode = parseInt(match[2], 10); } } var playItem = Object.assign({}, data, item); if (season != null) playItem.season = season; if (episode != null) playItem.episode = episode; var isSeries = isSeriesMedia(movie, playItem, data); var humanTitle = resolveMediaTitle(movie, data, playItem); var voiceLabel = formatVoiceLabel(playItem, isSeries, humanTitle); var sourceLabel = formatSourceLabel(sourceOverride || playItem.source || data.source || ''); var cleanFilename = normalizeLaunchMode(launchMode) === 'play'; var base = ''; if (isSeries && (season || episode)) { var titlePart = sanitizeFilenamePart(humanTitle) || 'Series'; base = titlePart + '-S' + pad2(season || 1) + '-E' + pad2(episode || 1); } else { var _titlePart = sanitizeFilenamePart(humanTitle) || 'Movie'; var year = resolveMovieYear(movie, data, playItem); base = year ? _titlePart + '-' + year : _titlePart; } if (!cleanFilename) { if (voiceLabel && DEFAULTS.filenameVoice) base = appendFilenameTag(base, voiceLabel); if (sourceLabel && DEFAULTS.filenameSource) base = appendFilenameTag(base, sourceLabel); } if (isSeries) { base = base.replace(/\s*\{tmdb-\d+\}\s*/gi, '').trim(); } else if (movie && movie.id && base.indexOf('{tmdb-') === -1) { base += ' {tmdb-' + movie.id + '}'; } return base + extension; } function getResumePosition(source) { if (!source || !source.timeline) return 0; var tl = source.timeline; if (tl.time != null && !isNaN(tl.time) && tl.time > 1) { return Math.max(0, Math.floor(tl.time)); } if (tl.percent != null && tl.duration != null && tl.duration > 0) { var percent = tl.percent > 1 ? tl.percent / 100 : tl.percent; return Math.max(0, Math.floor(tl.duration * percent)); } return 0; } function resolveLinkPosition(item, data, url) { var position = getResumePosition(item); if (position > 0) return position; if (data && url && data.url && compareStreamUrl(url, data.url)) { return getResumePosition(data); } return 0; } function linkToQueryPart(link, position) { var part = 'url=' + encodeURIComponent(link.url); if (position != null && !isNaN(position) && position > 0) { part += '&position=' + Math.floor(position); } if (link.filename) part += '&filename=' + encodeURIComponent(link.filename); if (link.sub) part += '&sub=' + encodeURIComponent(link.sub); return part; } function appendCallbacks(query, callbacks) { callbacks = callbacks || {}; if (callbacks.x_success) query += '&x-success=' + encodeURIComponent(callbacks.x_success); if (callbacks.x_error) query += '&x-error=' + encodeURIComponent(callbacks.x_error); return query; } function buildSaveQuery(links, callbacks, data) { var query = links.map(function (link) { return linkToQueryPart(link); }).join('&'); if (!isTorrentStream(data)) query += '&download=0'; return appendCallbacks(query, callbacks); } function buildPlayQuery(links, callbacks) { var query = links.map(function (link) { return linkToQueryPart(link, link.position); }).join('&'); return appendCallbacks(query, callbacks); } function buildAppleTvPlayUrl(links, callbacks) { // Несколько серий — flat url&position по доке Infuse (position на каждую) if (links.length > 1) { return PLAY_PREFIX + buildPlayQuery(links, callbacks); } var playlist = encodeURIComponent(JSON.stringify(links.map(function (item) { return { url: item.url, filename: item.filename }; }))); var query = linkToQueryPart(links[0], links[0].position) + '&playlist=' + playlist; return PLAY_PREFIX + appendCallbacks(query, callbacks); } function buildPlayLaunchUrl(playLinks, callbacks) { callbacks = resolveCallbacks(callbacks); if (Platform.is('apple_tv') === true) { return buildAppleTvPlayUrl(playLinks, callbacks); } return PLAY_PREFIX + buildPlayQuery(playLinks, callbacks); } function buildSaveAndPlayUrl(saveLinks, playLinks, data, callbacks) { var playUrl = buildPlayLaunchUrl(playLinks, callbacks); return SAVE_PREFIX + buildSaveQuery(saveLinks, { x_success: playUrl, x_error: callbacks.x_error }, data); } function buildLaunchUrl(saveLinks, playLinks, data, options) { playLinks = playLinks && playLinks.length ? playLinks : saveLinks; if (!playLinks.length) return ''; if (!saveLinks.length) saveLinks = playLinks; options = options || {}; var mode = normalizeLaunchMode(options.mode); var callbacks = resolveCallbacks(options); if (mode === 'save') { return SAVE_PREFIX + buildSaveQuery(saveLinks, callbacks, data); } if (mode === 'play') { return buildPlayLaunchUrl(playLinks, callbacks); } return buildSaveAndPlayUrl(saveLinks, playLinks, data, callbacks); } function buildLaunchUrlLength(saveLinks, playLinks, data, options) { options = options || {}; var mode = normalizeLaunchMode(options.mode); if (mode === 'play') { return buildLaunchUrl([], playLinks, data, options).length; } return buildLaunchUrl(saveLinks, playLinks, data, options).length; } function getPlaylistItems(data) { return resolvePlaylist(data).filter(function (item) { return typeof item.url === 'string'; }); } function findStartIndex(items, data) { if (!items.length) return 0; var index = findItemIndexInList(items, data); return index >= 0 ? index : 0; } function rotatePlayLinks(links, startIndex) { if (!startIndex || startIndex <= 0 || startIndex >= links.length) return links; return links.slice(startIndex).concat(links.slice(0, startIndex)); } function buildLink(url, item, movie, sourceName, data, launchMode) { var playItem = item || {}; if (!playItem.url) playItem.url = url; var streamUrl = sanitizeStreamUrl(url); return { url: streamUrl, filename: isTorrentStream(data) ? generateTorrentFilename(playItem, movie, data) : generateFilename(playItem, movie, sourceName, data, launchMode), sub: getSubtitleUrl(playItem, data), position: resolveLinkPosition(playItem, data, streamUrl) }; } function buildLinksForItems(data, items, movie, source, options, launchMode, startIndex) { startIndex = startIndex || 0; var links = []; for (var i = 0; i < items.length && links.length < options.maxItems; i++) { var playItem = enrichPlayItem(data, items[i]); var link = buildLink(items[i].url, playItem, movie, source, data, launchMode); var nextSave = links.concat([link]); var nextPlay = rotatePlayLinks(nextSave, startIndex); var probeLength = buildLaunchUrlLength(nextSave, nextPlay, data, { mode: launchMode, x_success: options.x_success, x_error: options.x_error }); if (probeLength > options.maxUrlLength && links.length) break; links.push(link); } return links; } function buildLinksFromPlayData(data, movie, options) { if (!data) return { saveLinks: [], playLinks: [] }; options = options || resolveOptions(data); var source = formatSourceLabel(options.source); var launchMode = normalizeLaunchMode(options.mode); var items = getPlaylistItems(data); if (options.seasonOnly && options.season) { var targetSeason = options.season; items = items.filter(function (item) { var meta = parseEpisodeMeta(item); if (meta.season == null && data.season != null) meta.season = data.season; return meta.season === targetSeason; }); } var startIndex = findStartIndex(items, data); var saveLinks = buildLinksForItems(data, items, movie, source, options, launchMode, startIndex); var playLinks = rotatePlayLinks(saveLinks, startIndex); if (!saveLinks.length && data.url) { var link = buildLink(data.url, enrichPlayItem(data, findPlayItem(data)), movie, source, data, launchMode); saveLinks = [link]; playLinks = [link]; } return { saveLinks: saveLinks, playLinks: playLinks }; } /** * Сборка infuse://x-callback-url/… по документации Firecore: * https://support.firecore.com/hc/ru/articles/215090997 * * play: url + filename + sub + position (сек) на каждую серию в плейлисте * save: без position (API Infuse) * save_and_play: save?…&x-success=encode(play?…) — онлайн * torrent: всегда play? + position из timeline (save/save_and_play → play в resolveInfuseMode) * * data.url — текущий поток * data.playlist — эпизоды (season, episode, url) * data.subtitles — [{url}] → параметр sub * data.source — в filename если DEFAULTS.filenameSource = true * DEFAULTS.filenameSource / filenameVoice — выкл/вкл суффиксы в имени */ function buildExternalUrl(data, callbacks) { if (!data || !data.url) return null; var options = resolveOptions(data, callbacks); var _buildLinksFromPlayDa = buildLinksFromPlayData(data, resolveCard$1(data), options), saveLinks = _buildLinksFromPlayDa.saveLinks, playLinks = _buildLinksFromPlayDa.playLinks; if (!saveLinks.length) return null; return buildLaunchUrl(saveLinks, playLinks, data, options); } function resolveCallbacks(callbacks) { var _Storage$field; callbacks = callbacks || {}; var appleTvClient = (_Storage$field = Storage.field('apple_tv_client')) !== null && _Storage$field !== void 0 ? _Storage$field : 'lampa'; return { x_success: callbacks.x_success || appleTvClient + '://infuseDidFinish', x_error: callbacks.x_error || appleTvClient + '://infuseDidFail' }; } function buildFallbackUrl(data, callbacks) { if (!data || !data.url) return null; callbacks = resolveCallbacks(callbacks); var movie = resolveCard$1(data); var playItem = enrichPlayItem(data, findPlayItem(data)); var source = formatSourceLabel(data.source || ''); var options = resolveOptions(data, callbacks); var link = buildLink(data.url, playItem, movie, source, data, options.mode); if (Platform.is('apple_tv') === true) { var query = linkToQueryPart(link, link.position); var playlist = serializePlaylist(data.playlist); if (playlist) query += '&playlist=' + playlist; return PLAY_PREFIX + appendCallbacks(query, callbacks); } return PLAY_PREFIX + buildPlayQuery([link], callbacks); } /** * Полный URL для Infuse: save_and_play + filenames, иначе простой play?url=… */ function resolveUrl(data, callbacks) { normalizePlayData(data); callbacks = resolveCallbacks(callbacks); return buildExternalUrl(data, callbacks) || buildFallbackUrl(data, callbacks); } var InfusePlayer = { buildExternalUrl: buildExternalUrl, buildFallbackUrl: buildFallbackUrl, buildLinksFromPlayData: buildLinksFromPlayData, buildLaunchUrl: buildLaunchUrl, generateFilename: generateFilename, formatSourceLabel: formatSourceLabel, isTorrentStream: isTorrentStream, resolveCard: resolveCard$1, resolveCallbacks: resolveCallbacks, resolveOptions: resolveOptions, resolveUrl: resolveUrl, normalizePlayData: normalizePlayData, serializePlaylist: serializePlaylist }; var html$e; var timer$3; var timer_hide; var message = []; var limit$2 = 4; /** * Имитация чата в плеере */ function init$M() { html$e = Template.elem('div', { "class": 'player-chat' }); Player.listener.follow('destroy', destroy$a); PlayerPanel.listener.follow('visible', function (e) { html$e.toggleClass('hide', !e.status); clearTimeout(timer_hide); }); } /** * Добавление сообщения в чат * @param {Object} data - Данные сообщения * @param {String} data.message - Текст сообщения * @param {String} data.icon - Иконка сообщения (HTML) * @param {Boolean} data.once - Если true, сообщение будет отображено только один раз */ function push$6(data) { if (data.once) { message = message.filter(function (m) { return m.from !== data.from; }); } message.push(data); if (message.length > limit$2) message.shift(); clearTimeout(timer$3); timer$3 = setTimeout(draw$3, 200); } /** * Отрисовка сообщений в чате */ function draw$3() { clear$7(); html$e.toggleClass('hide', false); timer_hide = setTimeout(function () { if (!PlayerPanel.visibleStatus()) html$e.toggleClass('hide', true); }, 5000); message.forEach(function (data) { var item = Template.elem('div', { "class": 'player-chat__message', children: [Template.elem('div', { "class": 'player-chat__icon', html: data.icon && typeof data.icon == 'string' ? data.icon : '' }), Template.elem('div', { "class": 'player-chat__body', children: [Template.elem('div', { "class": 'player-chat__text', html: data.message })] })] }); if (data.icon && _typeof(data.icon) == 'object') item.find('.player-chat__icon').empty().append(data.icon); if (!data.animated) { data.animated = true; item.addClass('animate'); } html$e.append(item); }); } /** * Очистка чата */ function clear$7() { html$e.empty(); } function render$8() { return html$e; } function destroy$a() { clear$7(); html$e.toggleClass('hide', true); clearTimeout(timer$3); clearTimeout(timer_hide); message = []; } var PlayerChat = { init: init$M, render: render$8, push: push$6, clear: clear$7, destroy: destroy$a }; var Line$5 = { onCreate: function onCreate() { this.html = Template.js('register'); this.html.addClass('selector register--line'); this.html.find('.register__name').text(this.data.title); this.html.find('.register__counter').text(this.data.count); if (this.data.limit) { this.html.find('.register__counter').append(Template.elem('span', { "class": 'register__limit', text: '/ ' + this.data.limit })); } } }; var Chart = { onCreate: function onCreate() { var _this = this; if (!this.data.chart) return; this.html.addClass('register--with-chart'); var chart = Template.elem('div', { "class": 'register__chart' }); this.data.chart.bars.forEach(function (height) { var bar = Template.elem('div', { "class": 'register__chart-bar' }); bar.style.height = height + '%'; if (_this.data.chart.threshold && height >= _this.data.chart.threshold) { bar.classList.add('register__chart-bar--threshold'); if (_this.data.chart.threshold_color) { bar.style.backgroundColor = _this.data.chart.threshold_color; } } chart.append(bar); }); this.html.prepend(chart); } }; var Tag = { onCreate: function onCreate() { this.html.addClass('register--tag'); if (typeof this.data.count !== 'undefined') { this.html.prepend(this.html.find('.register__counter')); } else { this.html.find('.register__counter').remove(); } } }; var Icon = { onCreate: function onCreate() { if (!this.data.icon) return; this.html.addClass('register--icon'); this.html.find('.register__name').prepend($(this.data.icon)); } }; var RegisterMap = { Line: Line$5, Chart: Chart, Tag: Tag, Icon: Icon, Callback: Callback$1 }; var Register = /*#__PURE__*/function (_Constructor) { _inherits(Register, _Constructor); var _super = _createSuper(Register); function Register() { _classCallCheck(this, Register); return _super.apply(this, arguments); } return Register; }(Constructor(RegisterMap)); var Helper$8 = new Mask(Arrays.getKeys(RegisterMap)); Helper$8.MASK.base = Helper$8.MASK.none; var html$d; var row; var graphs = []; var scroll$1; /** * Графики в плеере */ function init$L() { html$d = Template.elem('div', { "class": 'player-charts' }); Player.listener.follow('destroy', destroy$9); } /** * Добавление графика в плеер * @param {Object} data - Данные графика * @param {String} data.name - Имя графика * @param {String} data.type - Тип графика (segments) * @param {Array} data.data - Данные графика */ function push$5(data) { clear$6(data.name); graphs.push(data); draw$2(); if (!row) { row = PlayerFooter.appendRow(html$d); row.use({ onToggle: function onToggle() { var focus; $(this.html).find('.selector').each(function () { var segment = this.segment; var active = segment && PlayerVideo.video().currentTime >= segment.start && PlayerVideo.video().currentTime <= segment.end; if (active) focus = this; this.toggleClass('player-charts-segment--current', Boolean(active)); }); if (focus) { Controller.collectionFocus(focus, this.html); } } }); } PlayerPanel.visible(true); Controller.toggle('player_footer'); } /** * Отрисовка графиков в плеере */ function draw$2() { graphs.filter(function (g) { return !g.html; }).forEach(function (data) { var graph = Template.elem('div', { "class": 'player-charts__graph graph--' + data.type }); if (data.type == 'graph') { var segments = data.data || []; var segments_duration = 0; segments.forEach(function (segment) { if (segment.end > segments_duration) segments_duration = segment.end; }); var duration = Math.max(PlayerVideo.video().duration || 0, data.duration || segments_duration) || 1; var W = window.innerWidth - 30; var H = 210; var canvas = document.createElement('canvas'); canvas.width = W; canvas.height = H; canvas.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;display:block'; var ctx = canvas.getContext('2d'); var real = segments.map(function (segment) { return { x: (segment.start + segment.end) / 2 / duration * W, y: H - Math.min(100, segment.height || 5) / 100 * H }; }); if (real.length >= 2) { // Edge anchors + ghost points for smooth Catmull-Rom spline clamping var pts = [{ x: -W * 0.05, y: real[0].y }, { x: 0, y: real[0].y }].concat(_toConsumableArray(real), [{ x: W, y: real[real.length - 1].y }, { x: W * 1.05, y: real[real.length - 1].y }]); var curve = function curve() { for (var i = 1; i < pts.length - 2; i++) { var p0 = pts[i - 1], p1 = pts[i], p2 = pts[i + 1], p3 = pts[i + 2]; var cp1x = p1.x + (p2.x - p0.x) / 6; var cp1y = p1.y + (p2.y - p0.y) / 6; var cp2x = p2.x - (p3.x - p1.x) / 6; var cp2y = p2.y - (p3.y - p1.y) / 6; ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y); } }; var grad = ctx.createLinearGradient(0, 0, 0, H); grad.addColorStop(0, 'rgba(255,255,255,0.85)'); grad.addColorStop(0.55, 'rgba(200,200,200,0.3)'); grad.addColorStop(1, 'rgba(0,0,0,0)'); ctx.save(); ctx.beginPath(); ctx.moveTo(0, H); ctx.lineTo(0, pts[1].y); curve(); ctx.lineTo(W, H); ctx.closePath(); ctx.fillStyle = grad; ctx.fill(); ctx.restore(); ctx.save(); ctx.shadowColor = 'rgba(255,255,255,0.5)'; ctx.shadowBlur = 8; ctx.beginPath(); ctx.moveTo(0, pts[1].y); curve(); ctx.strokeStyle = 'rgba(255,255,255,0.9)'; ctx.lineWidth = 3; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.stroke(); ctx.restore(); } graph.append(canvas); } else if (data.type == 'segments') { var _segments = data.data || []; var _segments_duration = 0; _segments.forEach(function (segment) { if (segment.end > _segments_duration) _segments_duration = segment.end; }); var _duration = Math.max(PlayerVideo.video().duration, data.duration || _segments_duration); PlayerVideo.video().currentTime || 0; _segments.forEach(function (segment, i) { var width = (segment.end - segment.start) / _duration * 100; var height = (segment.height ? 11 * segment.height / 100 : 0.4).toFixed(2); var level = (segment.level ? 11 * segment.level / 100 : 0).toFixed(2); var segment_html = Template.elem('div', { "class": 'player-charts-segment', attrs: { style: 'width:' + width + '%; left:' + segment.start / _duration * 100 + '%' }, children: [Template.elem('div', { "class": 'player-charts-segment__bar', attrs: { style: 'height:' + height + 'em; margin-bottom:' + level + 'em' } })] }); var label = segment.label; var text = segment.title; if (label) segment_html.prepend(Template.elem('div', { "class": 'player-charts-segment__label', html: label })); if (segment.title && segment.description && width > 40) text += '
    ' + segment.description; if (text && width > 5) segment_html.prepend(Template.elem('div', { "class": 'player-charts-segment__text player-charts__text', children: [Template.elem('div', { html: text })] })); if (segment.onSelect) { segment_html.addClass('selector').on('hover:enter', function () { segment.onSelect(segment, segment_html); }); } if (segment.onFocus) { segment_html.on('hover:focus hover:hover hover:touch', function () { segment.onFocus(segment, segment_html); }); } segment_html.segment = segment; graph.append(segment_html); }); } else if (data.type == 'metadata') { var _segments2 = data.data || []; scroll$1 = new Scroll({ nopadding: true, horizontal: true }); _segments2.forEach(function (meter) { var register = Utils$1.createInstance(Register, meter, { module: Helper$8.toggle(Helper$8.MASK.base, 'Line', 'Chart', 'Icon', 'Callback') }); register.use({ onFocus: function onFocus() { scroll$1.update(register.render(), true); } }); register.create(); scroll$1.body().addClass('mapping--line'); scroll$1.append(register.render()); }); graph.append(scroll$1.render()); } data.html = graph; html$d.append(graph); }); } /** * Удаляет график по имени * @param {String} graph_name - Имя графика для удаления. Если не указано, удаляются все графики. */ function clear$6(graph_name) { if (graph_name) { var remove = graphs.filter(function (g) { return g.name == graph_name; }); remove.forEach(function (r) { Arrays.remove(graphs, r); r.html && r.html.remove(); }); } else { graphs.forEach(function (g) { g.html && g.html.remove(); }); graphs = []; } if (scroll$1) { scroll$1.destroy(); scroll$1 = null; } } function destroy$9() { clear$6(); if (row) { row.destroy(); row = null; } graphs = []; } var Charts = { init: init$L, push: push$5, clear: clear$6, destroy: destroy$9 }; var button; var agent_icon; var working = false; var play_data = {}; var network$5 = new Request(); var timers$1 = {}; var chat_history = []; var buttons$2 = []; var fields = [{ name: 'humor', color: '#f7e74a' }, { name: 'violence', color: '#f74a4a' }, { name: 'fear', color: '#e2b2ff' }, { name: 'romance', color: '#f74aa3' }, { name: 'sadness', color: '#f7a34a' }, { name: 'pace', color: '#4af74a' }, { name: 'action', color: '#ff8124' }, { name: 'sex', color: '#f74af7' }, { name: 'profanity', color: '#ff6262' }]; /** * Инициализация AI-агента */ function init$K() { agent_icon = Template.get('ai_agent', {}).addClass('animate ai-agent--absolute'); button = Template.elem('div', { "class": 'button player-panel__button--ai selector hide', html: '', children: [Template.elem('div', { "class": 'tooltip', text: Lang.translate('player_ai_agent_ask') })] }); button.on('hover:enter', function () { if (working) { PlayerChat.push({ from: 'ai', message: Lang.translate('player_ai_agent_working'), icon: agent_icon, once: true }); } else menu$2(); }); Html.elem('settings').after(button); Player.listener.follow('ready', start$8); Player.listener.follow('destroy', destroy$8); buttons$2 = [{ title: Lang.translate('player_ai_agent_ask_plot'), need: 'analysis' }, { title: Lang.translate('title_metadata'), need: 'metadata' }, { title: Lang.translate('player_ai_agent_ask_moods'), onSelect: function onSelect(a) { submenu(fields.map(function (field) { return { title: Lang.translate('title_meta_' + field.name), toagent: Lang.translate('player_ai_agent_ask_moods') + ' - ' + Lang.translate('title_meta_' + field.name), need: 'timeline', label: field.name }; }), Lang.translate('player_ai_agent_ask_moods')); } }, { title: Lang.translate('player_ai_agent_ask_highlights'), onSelect: function onSelect(a) { submenu(fields.map(function (field) { return { title: Lang.translate('title_meta_' + field.name), toagent: Lang.translate('player_ai_agent_ask_highlights') + ' - ' + Lang.translate('title_meta_' + field.name), need: 'moments/' + field.name }; }), Lang.translate('player_ai_agent_ask_highlights')); } }]; } function start$8(data) { var _play_data$card; play_data = {}; play_data.card = data.card || Activity.active().movie || Activity.active().card; var possibly = Account$1.hasPremium() || false; var type = (_play_data$card = play_data.card) !== null && _play_data$card !== void 0 && _play_data$card.original_name ? 'tv' : 'movie'; if (data.iptv || data.youtube) possibly = false;else if (!permit.token) possibly = false;else if (type == 'tv' && (!data.season || !data.episode)) possibly = false; if (!Platform.screen('tv')) possibly = false; play_data.type = type; if (possibly) { play_data.season = data.season || 0; play_data.episode = data.episode || 0; if (play_data.card) { var year = parseInt((play_data.card.release_date || play_data.card.first_air_date || '----').slice(0, 4)); if (year >= 1985) button.removeClass('hide'); } } chat_history.push({ html: $('
    ' + Lang.translate('player_ai_agent_info') + '
    '), noenter: true }); } function submenu(items, title) { Select.show({ title: title || Lang.translate('title_ai_assistant'), items: items, onSelect: function onSelect(a) { Controller.toggle('player_panel'); process$1(a); }, onBack: menu$2 }); } function menu$2() { chat_history.forEach(function (item) { return item.html.removeClass('selected').unbind(); }); if (buttons$2.find(function (i) { return i.selected; })) chat_history.forEach(function (item) { return item.selected = false; }); var items = [].concat(chat_history).concat(buttons$2); Select.show({ title: Lang.translate('title_ai_assistant'), items: items, nomark: true, onFocus: function onFocus(a) { items.forEach(function (item) { return item.selected = false; }); a.selected = true; }, onSelect: function onSelect(a) { items.forEach(function (item) { return item.selected = false; }); a.selected = true; Controller.toggle('player_panel'); process$1(a); }, onBack: function onBack() { Controller.toggle('player_panel'); } }); } function request(item) { clearTimeout(timers$1[item.need]); working = true; var url = 'ai/video/' + play_data.card.id + '/'; url += item.need + '?type=' + play_data.type + '&season=' + play_data.season + '&episode=' + play_data.episode; if (item.need.indexOf('moments') !== -1) url += '&limit=8'; Api$1.load(url).then(function (data) { if (data.status == 'processing') { timers$1[item.need] = setTimeout(function () { request(item); }, 5000); } else { PlayerChat.push({ from: 'ai', message: Lang.translate(data.status == 'completed' ? 'ready' : data.status == 'quota' ? 'player_ai_agent_quota' : 'player_ai_agent_no_analysis'), icon: agent_icon.removeClass('animate'), once: true }); if (data.status == 'completed') { draw$1(item, data); } working = false; } })["catch"](function (error) { PlayerChat.push({ from: 'ai', message: Lang.translate('player_ai_agent_no_analysis'), icon: agent_icon.removeClass('animate'), once: true }); working = false; }); } function draw$1(item, data) { if (item.need == 'analysis') { chat_history = []; chat_history.push({ title: item.toagent || item.title, html: $('
    ' + item.title + '
    '), noenter: true }); var result = data.chapters || data.segments; chat_history.forEach(function (item) { return item.selected = false; }); result.forEach(function (segment, i) { var text = segment.title; var time = '' + Utils$1.secondsToTimeHuman(segment.start_sec) + ''; var title = '' + segment.title + ''; var descr = '
    ' + segment.description + '
    '; var item = { title: text, html: $('
    ' + time + title + descr + '
    '), selected: i == 0, onSelect: function onSelect() { PlayerVideo.to(segment.start_sec); Controller.toggle('player_panel'); } }; chat_history.push(item); }); buttons$2.forEach(function (i) { i.selected = false; if (i.need == 'analysis') { i.ghost = true; i.noenter = true; } }); menu$2(); } else if (item.need == 'metadata') { var review = data.review || []; review.forEach(function (meter, i) { var _fields$find; var color = ((_fields$find = fields.find(function (f) { return f.name == meter.name; })) === null || _fields$find === void 0 ? void 0 : _fields$find.color) || '#fff'; meter.title = Lang.translate('title_meta_' + meter.name); meter.limit = 10; meter.count = meter.avg; meter.icon = ''; meter.chart = { threshold: 70, threshold_color: color, bars: meter.values.map(function (v) { return (v || 0) / 10 * 100; }) }; }); Charts.clear(); Charts.push({ name: item.need, type: 'metadata', data: review }); } else { var chart_data = []; if (item.need.indexOf('moments') !== -1) { var duration = PlayerVideo.video().duration; var total = data.segments.length; data.segments.forEach(function (segment, i) { var start = 0; var end = 0; if (total > 1) { start = duration * i / total; end = start + duration / total; } else { var size = duration * 0.5; start = segment.start_sec > duration - size ? duration - size : segment.start_sec; end = start + size; } chart_data.push({ start: Math.round(start), end: Math.round(end), title: segment.title, description: segment.description, label: Utils$1.secondsToTimeHuman(segment.start_sec), onSelect: function onSelect() { PlayerVideo.to(segment.start_sec); } }); }); } else if (item.need == 'timeline') { data.segments.forEach(function (segment, i) { var score = segment[item.label]; chart_data.push({ start: segment.start_sec, end: segment.end_sec, title: segment.title, description: segment.description, height: score / 10 * 100, label: score > 1 ? score : '', onSelect: score > 1 ? function () { PlayerVideo.to(segment.start_sec); } : undefined, onFocus: function onFocus() { PlayerChat.push({ from: 'ai', message: Utils$1.secondsToTimeHuman(segment.start_sec) + '
    ' + segment.title, icon: agent_icon.removeClass('animate'), once: true }); } }); }); } if (chart_data.length) { Charts.clear(); Charts.push({ name: item.need, type: 'segments', data: chart_data }); } else { PlayerChat.push({ from: 'ai', message: Lang.translate('player_ai_agent_no_data'), icon: agent_icon.removeClass('animate'), once: true }); } } } function process$1(item) { var user_icon = Profile.icon(); PlayerChat.push({ from: 'user', message: item.toagent || item.title, icon: user_icon ? '' : '', once: true }); PlayerChat.push({ from: 'ai', message: Lang.translate('player_ai_agent_processing'), icon: agent_icon.addClass('animate'), once: true }); request(item); } function destroy$8() { button.addClass('hide'); play_data = {}; chat_history = []; network$5.clear(); for (var key in timers$1) { clearTimeout(timers$1[key]); } working = false; buttons$2.forEach(function (i) { i.selected = false; i.ghost = false; i.noenter = false; }); } var Agent = { init: init$K, destroy: destroy$8 }; var work$1 = null; var timer_ask = null; var timer_save = null; function init$J() { PlayerVideo.listener.follow('reset_continue', function () { if (work$1 && work$1.timeline && !work$1.timeline.continued_bloc) work$1.timeline.continued = false; }); PlayerVideo.listener.follow('timeupdate', function (e) { if (!work$1 || !work$1.timeline || work$1.timeline.waiting_for_user || work$1.timeline.stop_recording || !e.duration) return; if (Storage.field('player_timecode') !== 'again' && !work$1.timeline.continued) { var exact = parseFloat(work$1.timeline.time + ''); exact = isNaN(exact) ? 0 : parseFloat(exact.toFixed(3)); var prend = e.duration - 15, posit = exact > 0 && exact < e.duration ? exact : Math.round(e.duration * work$1.timeline.percent / 100); if (posit > 10 && work$1.timeline.percent < 90) PlayerVideo.to(posit > prend ? prend : posit); work$1.timeline.continued = true; } else { work$1.timeline.percent = Math.round(e.current / e.duration * 100); work$1.timeline.time = e.current; work$1.timeline.duration = e.duration; } }); Player.listener.follow('start', start$7); } function start$7(data) { work$1 = data; if (work$1 && work$1.timeline) work$1.timeline.continued = false; startSaveLoop(); } function needToContinue(onToggle) { if (work$1 && work$1.timeline && work$1.timeline.percent) { work$1.timeline.waiting_for_user = false; if (Storage.field('player_timecode') == 'ask') { work$1.timeline.waiting_for_user = true; Select.show({ title: Lang.translate('title_action'), items: [{ title: Lang.translate('player_start_from') + ' ' + Utils$1.secondsToTime(work$1.timeline.time) + '?', yes: true }, { title: Lang.translate('settings_param_no') }], onBack: function onBack() { work$1.timeline.continued = true; work$1.timeline.continued_bloc = true; onToggle(); clearTimeout(timer_ask); }, onSelect: function onSelect(a) { work$1.timeline.waiting_for_user = false; if (!a.yes) { work$1.timeline.continued = true; work$1.timeline.continued_bloc = true; } onToggle(); clearTimeout(timer_ask); } }); clearTimeout(timer_ask); timer_ask = setTimeout(function () { work$1.timeline.continued = true; work$1.timeline.continued_bloc = true; Select.hide(); onToggle(); }, 8000); } } } function setEnd() { if (work$1 && work$1.timeline) { work$1.timeline.waiting_for_user = true; work$1.timeline.percent = 100; work$1.timeline.time = work$1.timeline.duration || 0; } } function resetContinue() { if (work$1 && work$1.timeline) { work$1.timeline.continued = false; work$1.timeline.continued_bloc = false; } } function saveTimeView() { if (work$1 && work$1.timeline && work$1.timeline.handler && !work$1.timeline.stop_recording) { work$1.timeline.handler(work$1.timeline.percent, work$1.timeline.time, work$1.timeline.duration); } } function startSaveLoop() { if (work$1 && work$1.timeline && !work$1.timeline.stop_recording) { timer_save = setInterval(saveTimeView, 1000 * 60 * 2); } } function setRecording(status) { if (work$1 && work$1.timeline) { work$1.timeline.stop_recording = !status; } } function destroy$7() { saveTimeView(); if (work$1 && work$1.timeline) work$1.timeline.stop_recording = false; clearTimeout(timer_ask); clearInterval(timer_save); work$1 = null; } var Timeline = { init: init$J, needToContinue: needToContinue, setEnd: setEnd, resetContinue: resetContinue, saveTimeView: saveTimeView, startSaveLoop: startSaveLoop, setRecording: setRecording, destroy: destroy$7 }; var FORMAT_NAME$7 = "vtt"; var helper$5 = { toMilliseconds: function toMilliseconds(s) { var match = /^\s*(\d{1,2}:)?(\d{1,2}):(\d{1,2})([.,](\d{1,3}))?\s*$/.exec(s); var hh = match[1] ? parseInt(match[1].replace(":", "")) : 0; var mm = parseInt(match[2]); var ss = parseInt(match[3]); var ff = match[5] ? parseInt(match[5]) : 0; var ms = hh * 3600 * 1000 + mm * 60 * 1000 + ss * 1000 + ff; return ms; }, toTimeString: function toTimeString(ms) { var hh = Math.floor(ms / 1000 / 3600); var mm = Math.floor(ms / 1000 / 60 % 60); var ss = Math.floor(ms / 1000 % 60); var ff = Math.floor(ms % 1000); var time = (hh < 10 ? "0" : "") + hh + ":" + (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss + "." + (ff < 100 ? "0" : "") + (ff < 10 ? "0" : "") + ff; return time; } }; /****************************************************************************************** * Parses captions in WebVTT format (Web Video Text Tracks Format) ******************************************************************************************/ function parse$9(content, options) { var index = 1; var captions = []; var eol = options.eol || "\r\n"; var parts = content.split(/\r?\n\s+\r?\n/); for (var i = 0; i < parts.length; i++) { //WebVTT data var regex = /^([^\r\n]+\r?\n)?((\d{1,2}:)?\d{1,2}:\d{1,2}([.,]\d{1,3})?)\s*\-\-\>\s*((\d{1,2}:)?\d{1,2}:\d{1,2}([.,]\d{1,3})?)\r?\n([\s\S]*)(\r?\n)*$/gi; var match = regex.exec(parts[i]); if (match) { var caption = {}; caption.type = "caption"; caption.index = index++; if (match[1]) { caption.cue = match[1].replace(/[\r\n]*/gi, ""); } caption.start = helper$5.toMilliseconds(match[2]); caption.end = helper$5.toMilliseconds(match[5]); caption.duration = caption.end - caption.start; var lines = match[8].split(/\r?\n/); caption.content = lines.join(eol); caption.text = caption.content.replace(/\<[^\>]+\>/g, "") //bold or italic .replace(/\{[^\}]+\}/g, ""); //{b}bold{/b} or {i}italic{/i} captions.push(caption); continue; } //WebVTT meta var meta = /^([A-Z]+)(\r?\n([\s\S]*))?$/.exec(parts[i]); if (!meta) { //Try inline meta meta = /^([A-Z]+)\s+([^\r\n]*)?$/.exec(parts[i]); } if (meta) { var caption = {}; caption.type = "meta"; caption.name = meta[1]; if (meta[3]) { caption.data = meta[3]; } captions.push(caption); continue; } if (options.verbose) { console.log("WARN: Unknown part", parts[i]); } } return captions; } /****************************************************************************************** * Builds captions in WebVTT format (Web Video Text Tracks Format) ******************************************************************************************/ function build$9(captions, options) { var eol = options.eol || "\r\n"; var content = "WEBVTT" + eol + eol; for (var i = 0; i < captions.length; i++) { var caption = captions[i]; if (caption.type == "meta") { if (caption.name == "WEBVTT") continue; content += caption.name + eol; content += caption.data ? caption.data + eol : ""; content += eol; continue; } if (typeof caption.type === "undefined" || caption.type == "caption") { content += (i + 1).toString() + eol; content += helper$5.toTimeString(caption.start) + " --> " + helper$5.toTimeString(caption.end) + eol; content += caption.text + eol; content += eol; continue; } if (options.verbose) { console.log("SKIP:", caption); } } return content; } /****************************************************************************************** * Detects a subtitle format from the content. ******************************************************************************************/ function detect$8(content) { if (typeof content !== "string") { throw new Error("Expected string content!"); } if (/^[\s\r\n]*WEBVTT\r?\n/g.test(content)) { /* WEBVTT ... */ return "vtt"; } } /****************************************************************************************** * Export ******************************************************************************************/ var vttFormatter = { name: FORMAT_NAME$7, helper: helper$5, detect: detect$8, parse: parse$9, build: build$9 }; var FORMAT_NAME$6 = "lrc"; var helper$4 = { toMilliseconds: function toMilliseconds(s) { var match = /^\s*(\d+):(\d{1,2})([.,](\d{1,3}))?\s*$/.exec(s); var mm = parseInt(match[1]); var ss = parseInt(match[2]); var ff = match[4] ? parseInt(match[4]) : 0; var ms = mm * 60 * 1000 + ss * 1000 + ff * 10; return ms; }, toTimeString: function toTimeString(ms) { var mm = Math.floor(ms / 1000 / 60); var ss = Math.floor(ms / 1000 % 60); var ff = Math.floor(ms % 1000); var time = (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss + "." + (ff < 100 ? "0" : "") + (ff < 10 ? "0" : Math.floor(ff / 10)); return time; } }; /****************************************************************************************** * Parses captions in LRC format: https://en.wikipedia.org/wiki/LRC_%28file_format%29 ******************************************************************************************/ function parse$8(content, options) { var prev = null; var captions = []; options.eol || "\r\n"; var parts = content.split(/\r?\n/); for (var i = 0; i < parts.length; i++) { if (!parts[i] || parts[i].trim().length == 0) { continue; } //LRC content var regex = /^\[(\d{1,2}:\d{1,2}([.,]\d{1,3})?)\](.*)(\r?\n)*$/gi; var match = regex.exec(parts[i]); if (match) { var caption = {}; caption.type = "caption"; caption.start = helper$4.toMilliseconds(match[1]); caption.end = caption.start + 2000; caption.duration = caption.end - caption.start; caption.content = match[3]; caption.text = caption.content; captions.push(caption); //Update previous if (prev) { prev.end = caption.start; prev.duration = prev.end - prev.start; } prev = caption; continue; } //LRC meta var meta = /^\[([\w\d]+):([^\]]*)\](\r?\n)*$/gi.exec(parts[i]); if (meta) { var caption = {}; caption.type = "meta"; caption.tag = meta[1]; if (meta[2]) { caption.data = meta[2]; } captions.push(caption); continue; } if (options.verbose) { console.log("WARN: Unknown part", parts[i]); } } return captions; } /****************************************************************************************** * Builds captions in LRC format: https://en.wikipedia.org/wiki/LRC_%28file_format%29 ******************************************************************************************/ function build$8(captions, options) { var content = ""; var lyrics = false; var eol = options.eol || "\r\n"; for (var i = 0; i < captions.length; i++) { var caption = captions[i]; if (caption.type == "meta") { if (caption.tag && caption.data) { content += "[" + caption.tag + ":" + caption.data.replace(/[\r\n]+/g, " ") + "]" + eol; } continue; } if (typeof caption.type === "undefined" || caption.type == "caption") { if (!lyrics) { content += eol; //New line when lyrics start lyrics = true; } content += "[" + helper$4.toTimeString(caption.start) + "]" + caption.text + eol; continue; } if (options.verbose) { console.log("SKIP:", caption); } } return content; } /****************************************************************************************** * Detects a subtitle format from the content. ******************************************************************************************/ function detect$7(content) { if (typeof content === "string") { if (/\r?\n\[(\d+:\d{1,2}([.,]\d{1,3})?)\](.*)\r?\n/.test(content)) { /* [04:48.28]Sister, perfume? */ //return "lrc"; return true; } } } /****************************************************************************************** * Export ******************************************************************************************/ var lrcFormatter = { name: FORMAT_NAME$6, helper: helper$4, detect: detect$7, parse: parse$8, build: build$8 }; var FORMAT_NAME$5 = "smi"; var helper$3 = { htmlEncode: function htmlEncode(text) { return text.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/\/g, '>') //.replace(/\s/g, ' ') .replace(/\r?\n/g, '
    '); }, htmlDecode: function htmlDecode(html, eol) { return html.replace(/\/gi, eol || '\r\n').replace(/ /g, ' ').replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); } }; /****************************************************************************************** * Parses captions in SAMI format (.smi) ******************************************************************************************/ function parse$7(content, options) { var captions = []; var eol = options.eol || "\r\n"; var title = /\]*\>([\s\S]*)\<\/TITLE\>/gi.exec(content); if (title) { var caption = {}; caption.type = "meta"; caption.name = "title"; caption.data = title[1].replace(/^[\s\r\n]*/g, "").replace(/[\s\r\n]*$/g, ""); captions.push(caption); } var style = /\]*\>([\s\S]*)\<\/STYLE\>/gi.exec(content); if (style) { var caption = {}; caption.type = "meta"; caption.name = "style"; caption.data = style[1]; captions.push(caption); } var sami = content.replace(/^[\s\S]*\]*\>/gi, "") //Remove content before body .replace(/\<\/BODY[^\>]*\>[\s\S]*$/gi, ""); //Remove content after body var prev = null; var parts = sami.split(/\ var match = /^\]+Start\s*=\s*["']?(\d+)["']?[^\>]*\>([\s\S]*)/gi.exec(part); if (match) { var caption = {}; caption.type = "caption"; caption.start = parseInt(match[1]); caption.end = caption.start + 2000; caption.duration = caption.end - caption.start; caption.content = match[2].replace(/^\<\/SYNC[^\>]*>/gi, ""); var blank = true; var p = /^\]+Class\s*=\s*["']?([\w\d\-_]+)["']?[^\>]*\>([\s\S]*)/gi.exec(caption.content); if (!p) { p = /^\]*)\>([\s\S]*)/gi.exec(caption.content); } if (p) { var html = p[2].replace(/\ tag html = html.replace(/\[\s\r\n]+/gi, eol).replace(/\/gi, eol).replace(/\<[^\>]+\>/g, ""); //Remove all tags html = html.replace(/^[\s\r\n]+/g, "").replace(/[\s\r\n]+$/g, ""); //Trim new lines and spaces blank = html.replace(/ /gi, " ").replace(/[\s\r\n]+/g, "").length == 0; caption.text = helper$3.htmlDecode(html, eol); } if (!options.preserveSpaces && blank) { if (options.verbose) { console.log("INFO: Skipping white space caption at " + caption.start); } } else { captions.push(caption); } //Update previous if (prev) { prev.end = caption.start; prev.duration = prev.end - prev.start; } prev = caption; continue; } if (options.verbose) { console.log("WARN: Unknown part", parts[i]); } } return captions; } /****************************************************************************************** * Builds captions in SAMI format (.smi) ******************************************************************************************/ function build$7(captions, options) { var eol = options.eol || "\r\n"; var content = ""; content += '' + eol; content += '' + eol; content += '' + (options.title || "") + '' + eol; content += '' + eol; content += '' + eol; content += '' + eol; for (var i = 0; i < captions.length; i++) { var caption = captions[i]; if (caption.type == "meta") { continue; } if (typeof caption.type === "undefined" || caption.type == "caption") { //Start of caption content += '' + eol; content += '

    ' + helper$3.htmlEncode(caption.text || "") + (options.closeTags ? '

    ' : "") + eol; if (options.closeTags) { content += '
    ' + eol; } //Blank line indicates the end of caption content += '' + eol; content += '

    ' + ' ' + (options.closeTags ? '

    ' : "") + eol; if (options.closeTags) { content += '
    ' + eol; } continue; } if (options.verbose) { console.log("SKIP:", caption); } } content += '' + eol; content += '
    ' + eol; return content; } /****************************************************************************************** * Detects a subtitle format from the content. ******************************************************************************************/ function detect$6(content) { if (typeof content === "string") { if (/\]*\>[\s\S]*\]*\>/g.test(content)) { /* */ return "smi"; } } } /****************************************************************************************** * Export ******************************************************************************************/ var smiFormatter = { name: FORMAT_NAME$5, helper: helper$3, detect: detect$6, parse: parse$7, build: build$7 }; var FORMAT_NAME$4 = "ssa"; var helper$2 = { toMilliseconds: function toMilliseconds(s) { var match = /^\s*(\d+:)?(\d{1,2}):(\d{1,2})([.,](\d{1,3}))?\s*$/.exec(s); var hh = match[1] ? parseInt(match[1].replace(":", "")) : 0; var mm = parseInt(match[2]); var ss = parseInt(match[3]); var ff = match[5] ? parseInt(match[5]) : 0; var ms = hh * 3600 * 1000 + mm * 60 * 1000 + ss * 1000 + ff * 10; return ms; }, toTimeString: function toTimeString(ms) { var hh = Math.floor(ms / 1000 / 3600); var mm = Math.floor(ms / 1000 / 60 % 60); var ss = Math.floor(ms / 1000 % 60); var ff = Math.floor(ms % 1000 / 10); //2 digits var time = hh + ":" + (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss + "." + (ff < 10 ? "0" : "") + ff; return time; } }; /****************************************************************************************** * Parses captions in SubStation Alpha format (.ssa) ******************************************************************************************/ function parse$6(content, options) { var meta; var columns = null; var captions = []; var eol = options.eol || "\r\n"; var parts = content.split(/\r?\n\s*\r?\n/); for (var i = 0; i < parts.length; i++) { var regex = /^\s*\[([^\]]+)\]\r?\n([\s\S]*)(\r?\n)*$/gi; var match = regex.exec(parts[i]); if (match) { var tag = match[1]; var lines = match[2].split(/\r?\n/); for (var l = 0; l < lines.length; l++) { var line = lines[l]; if (/^\s*;/.test(line)) { continue; //Skip comment } var m = /^\s*([^:]+):\s*(.*)(\r?\n)?$/.exec(line); if (m) { if (tag == "Script Info") { if (!meta) { meta = {}; meta.type = "meta"; meta.data = {}; captions.push(meta); } var name = m[1].trim(); var value = m[2].trim(); meta.data[name] = value; continue; } if (tag == "V4 Styles" || tag == "V4+ Styles") { var name = m[1].trim(); var value = m[2].trim(); if (name == "Format") { columns = value.split(/\s*,\s*/g); continue; } if (name == "Style") { var values = value.split(/\s*,\s*/g); var caption = {}; caption.type = "style"; caption.data = {}; for (var c = 0; c < columns.length && c < values.length; c++) { caption.data[columns[c]] = values[c]; } captions.push(caption); continue; } } if (tag == "Events") { var name = m[1].trim(); var value = m[2].trim(); if (name == "Format") { columns = value.split(/\s*,\s*/g); continue; } if (name == "Dialogue") { //Work-around for missing text (when the text contains ',' char) var getPosition = function getPosition(s, search, index) { return s.split(search, index).join(search).length; }; var values = value.split(/\s*,\s*/g); var caption = {}; caption.type = "caption"; caption.data = {}; for (var c = 0; c < columns.length && c < values.length; c++) { caption.data[columns[c]] = values[c]; } caption.start = helper$2.toMilliseconds(caption.data["Start"]); caption.end = helper$2.toMilliseconds(caption.data["End"]); caption.duration = caption.end - caption.start; caption.content = caption.data["Text"]; var indexOfText = getPosition(value, ',', columns.length - 1) + 1; caption.content = value.substr(indexOfText); caption.data["Text"] = caption.content; caption.text = caption.content.replace(/\\N/g, eol) //"\N" for new line .replace(/\{[^\}]+\}/g, ""); //{\pos(400,570)} captions.push(caption); continue; } } } } } if (options.verbose) { console.log("WARN: Unknown part", parts[i]); } } return captions; } /****************************************************************************************** * Builds captions in SubStation Alpha format (.ssa) ******************************************************************************************/ function build$6(captions, options) { var eol = options.eol || "\r\n"; var ass = options.format == "ass"; var content = ""; content += "[Script Info]" + eol; content += "; Script generated by subsrt " + eol; content += "ScriptType: v4.00" + (ass ? "+" : "") + eol; content += "Collisions: Normal" + eol; content += eol; if (ass) { content += "[V4+ Styles]" + eol; content += "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding" + eol; content += "Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,&H80000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0" + eol; } else { content += "[V4 Styles]" + eol; content += "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding" + eol; content += "Style: DefaultVCD, Arial,28,11861244,11861244,11861244,-2147483640,-1,0,1,1,2,2,30,30,30,0,0" + eol; } content += eol; content += "[Events]" + eol; content += "Format: " + (ass ? "Layer" : "Marked") + ", Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" + eol; for (var i = 0; i < captions.length; i++) { var caption = captions[i]; if (caption.type == "meta") { continue; } if (typeof caption.type === "undefined" || caption.type == "caption") { content += "Dialogue: " + (ass ? "0" : "Marked=0") + "," + helper$2.toTimeString(caption.start) + "," + helper$2.toTimeString(caption.end) + ",DefaultVCD, NTP,0000,0000,0000,," + caption.text.replace(/\r?\n/g, "\\N") + eol; continue; } if (options.verbose) { console.log("SKIP:", caption); } } return content; } /****************************************************************************************** * Detects a subtitle format from the content. ******************************************************************************************/ function detect$5(content) { if (typeof content !== "string") { throw new Error("Expected string content!"); } if (/^[\s\r\n]*\[Script Info\]\r?\n/g.test(content) && /[\s\r\n]*\[Events\]\r?\n/g.test(content)) { /* [Script Info] ... [Events] */ //Advanced (V4+) styles for ASS format return content.indexOf("[V4+ Styles]") > 0 ? "ass" : "ssa"; } } /****************************************************************************************** * Export ******************************************************************************************/ var ssaFormatter = { name: FORMAT_NAME$4, helper: helper$2, detect: detect$5, parse: parse$6, build: build$6 }; var assFormatter = { name: "ass", helper: ssaFormatter.helper, detect: ssaFormatter.detect, parse: ssaFormatter.parse, build: ssaFormatter.build }; var FORMAT_NAME$3 = "sub"; var DEFAULT_FPS = 25; /****************************************************************************************** * Parses captions in MicroDVD format: https://en.wikipedia.org/wiki/MicroDVD ******************************************************************************************/ function parse$5(content, options) { var fps = options.fps > 0 ? options.fps : DEFAULT_FPS; var captions = []; var eol = options.eol || "\r\n"; var parts = content.split(/\r?\n/g); for (var i = 0; i < parts.length; i++) { var regex = /^\{(\d+)\}\{(\d+)\}(.*)$/gi; var match = regex.exec(parts[i]); if (match) { var caption = {}; caption.type = "caption"; caption.index = i + 1; caption.frame = { start: parseInt(match[1]), end: parseInt(match[2]) }; caption.frame.count = caption.frame.end - caption.frame.start; caption.start = Math.round(caption.frame.start / fps); caption.end = Math.round(caption.frame.end / fps); caption.duration = caption.end - caption.start; var lines = match[3].split(/\|/g); caption.content = lines.join(eol); caption.text = caption.content.replace(/\{[^\}]+\}/g, ""); //{0}{25}{c:$0000ff}{y:b,u}{f:DeJaVuSans}{s:12}Hello! captions.push(caption); continue; } if (options.verbose) { console.log("WARN: Unknown part", parts[i]); } } return captions; } /****************************************************************************************** * Builds captions in MicroDVD format: https://en.wikipedia.org/wiki/MicroDVD ******************************************************************************************/ function build$5(captions, options) { var fps = options.fps > 0 ? options.fps : DEFAULT_FPS; var sub = ""; var eol = options.eol || "\r\n"; for (var i = 0; i < captions.length; i++) { var caption = captions[i]; if (typeof caption.type === "undefined" || caption.type == "caption") { var startFrame = _typeof(caption.frame) == "object" && caption.frame.start >= 0 ? caption.frame.start : caption.start * fps; var endFrame = _typeof(caption.frame) == "object" && caption.frame.end >= 0 ? caption.frame.end : caption.end * fps; var text = caption.text.replace(/\r?\n/, "|"); sub += "{" + startFrame + "}" + "{" + endFrame + "}" + text + eol; continue; } if (options.verbose) { console.log("SKIP:", caption); } } return sub; } /****************************************************************************************** * Detects a subtitle format from the content. ******************************************************************************************/ function detect$4(content) { if (typeof content === "string") { if (/^\{\d+\}\{\d+\}(.*)/.test(content)) { /* {7207}{7262}Sister, perfume? */ return FORMAT_NAME$3; } } } /****************************************************************************************** * Export ******************************************************************************************/ var subFormatter = { name: FORMAT_NAME$3, detect: detect$4, parse: parse$5, build: build$5 }; var FORMAT_NAME$2 = "srt"; var helper$1 = { toMilliseconds: function toMilliseconds(s) { var match = /^\s*(\d{1,2}):(\d{1,2}):(\d{1,2})([.,](\d{1,3}))?\s*$/.exec(s); var hh = parseInt(match[1]); var mm = parseInt(match[2]); var ss = parseInt(match[3]); var ff = match[5] ? parseInt(match[5]) : 0; var ms = hh * 3600 * 1000 + mm * 60 * 1000 + ss * 1000 + ff; return ms; }, toTimeString: function toTimeString(ms) { var hh = Math.floor(ms / 1000 / 3600); var mm = Math.floor(ms / 1000 / 60 % 60); var ss = Math.floor(ms / 1000 % 60); var ff = Math.floor(ms % 1000); var time = (hh < 10 ? "0" : "") + hh + ":" + (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss + "," + (ff < 100 ? "0" : "") + (ff < 10 ? "0" : "") + ff; return time; } }; /****************************************************************************************** * Parses captions in SubRip format (.srt) ******************************************************************************************/ function parse$4(content, options) { var captions = []; var eol = options.eol || "\r\n"; var parts = content.split(/\r?\n\s+\r?\n/g); for (var i = 0; i < parts.length; i++) { var regex = /^(\d+)\r?\n(\d{1,2}:\d{1,2}:\d{1,2}([.,]\d{1,3})?)\s*\-\-\>\s*(\d{1,2}:\d{1,2}:\d{1,2}([.,]\d{1,3})?)\r?\n([\s\S]*)(\r?\n)*$/gi; var match = regex.exec(parts[i]); if (match) { var caption = {}; caption.type = "caption"; caption.index = parseInt(match[1]); caption.start = helper$1.toMilliseconds(match[2]); caption.end = helper$1.toMilliseconds(match[4]); caption.duration = caption.end - caption.start; var lines = match[6].split(/\r?\n/); caption.content = lines.join(eol); caption.text = caption.content.replace(/\<[^\>]+\>/g, "") //bold or italic .replace(/\{[^\}]+\}/g, "") //{b}bold{/b} or {i}italic{/i} .replace(/\>\>\s*[^:]*:\s*/g, ""); //>> SPEAKER NAME: captions.push(caption); continue; } if (options.verbose) { console.log("WARN: Unknown part", parts[i]); } } return captions; } /****************************************************************************************** * Builds captions in SubRip format (.srt) ******************************************************************************************/ function build$4(captions, options) { var srt = ""; var eol = options.eol || "\r\n"; for (var i = 0; i < captions.length; i++) { var caption = captions[i]; if (typeof caption.type === "undefined" || caption.type == "caption") { srt += (i + 1).toString() + eol; srt += helper$1.toTimeString(caption.start) + " --> " + helper$1.toTimeString(caption.end) + eol; srt += caption.text + eol; srt += eol; continue; } if (options.verbose) { console.log("SKIP:", caption); } } return srt; } /****************************************************************************************** * Detects a subtitle format from the content. ******************************************************************************************/ function detect$3(content) { if (typeof content === "string") { if (/\d+\r?\n\d{1,2}:\d{1,2}:\d{1,2}([.,]\d{1,3})?\s*\-\-\>\s*\d{1,2}:\d{1,2}:\d{1,2}([.,]\d{1,3})?/g.test(content)) { /* 3 00:04:48,280 --> 00:04:50,510 Sister, perfume? */ return FORMAT_NAME$2; } } } /****************************************************************************************** * Export ******************************************************************************************/ var srtFormatter = { name: FORMAT_NAME$2, helper: helper$1, detect: detect$3, parse: parse$4, build: build$4 }; var FORMAT_NAME$1 = "sbv"; var helper = { toMilliseconds: function toMilliseconds(s) { var match = /^\s*(\d{1,2}):(\d{1,2}):(\d{1,2})([.,](\d{1,3}))?\s*$/.exec(s); var hh = parseInt(match[1]); var mm = parseInt(match[2]); var ss = parseInt(match[3]); var ff = match[5] ? parseInt(match[5]) : 0; var ms = hh * 3600 * 1000 + mm * 60 * 1000 + ss * 1000 + ff; return ms; }, toTimeString: function toTimeString(ms) { var hh = Math.floor(ms / 1000 / 3600); var mm = Math.floor(ms / 1000 / 60 % 60); var ss = Math.floor(ms / 1000 % 60); var ff = Math.floor(ms % 1000); var time = (hh < 10 ? "0" : "") + hh + ":" + (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss + "." + (ff < 100 ? "0" : "") + (ff < 10 ? "0" : "") + ff; return time; } }; /****************************************************************************************** * Parses captions in SubViewer format (.sbv) ******************************************************************************************/ function parse$3(content, options) { var captions = []; var eol = options.eol || "\r\n"; var parts = content.split(/\r?\n\s+\r?\n/); for (var i = 0; i < parts.length; i++) { var regex = /^(\d{1,2}:\d{1,2}:\d{1,2}([.,]\d{1,3})?)\s*[,;]\s*(\d{1,2}:\d{1,2}:\d{1,2}([.,]\d{1,3})?)\r?\n([\s\S]*)(\r?\n)*$/gi; var match = regex.exec(parts[i]); if (match) { var caption = {}; caption.type = "caption"; caption.start = helper.toMilliseconds(match[1]); caption.end = helper.toMilliseconds(match[3]); caption.duration = caption.end - caption.start; var lines = match[5].split(/\[br\]|\r?\n/gi); caption.content = lines.join(eol); caption.text = caption.content.replace(/\>\>\s*[^:]+:\s*/g, ""); //>> SPEAKER NAME: captions.push(caption); continue; } if (options.verbose) { console.log("WARN: Unknown part", parts[i]); } } return captions; } /****************************************************************************************** * Builds captions in SubViewer format (.sbv) ******************************************************************************************/ function build$3(captions, options) { var content = ""; var eol = options.eol || "\r\n"; for (var i = 0; i < captions.length; i++) { var caption = captions[i]; if (typeof caption.type === "undefined" || caption.type == "caption") { content += helper.toTimeString(caption.start) + "," + helper.toTimeString(caption.end) + eol; content += caption.text + eol; content += eol; continue; } if (options.verbose) { console.log("SKIP:", caption); } } return content; } /****************************************************************************************** * Detects a subtitle format from the content. ******************************************************************************************/ function detect$2(content) { if (typeof content !== "string") { throw new Error("Expected string content!"); } if (/\d{1,2}:\d{1,2}:\d{1,2}([.,]\d{1,3})?\s*[,;]\s*\d{1,2}:\d{1,2}:\d{1,2}([.,]\d{1,3})?/g.test(content)) { /* 00:04:48.280,00:04:50.510 Sister, perfume? */ return "sbv"; } } /****************************************************************************************** * Export ******************************************************************************************/ var sbvFormatter = { name: FORMAT_NAME$1, helper: helper, detect: detect$2, parse: parse$3, build: build$3 }; var FORMAT_NAME = "json"; /****************************************************************************************** * Parses captions in JSON format ******************************************************************************************/ function parse$2(content, options) { return JSON.parse(content); } /****************************************************************************************** * Builds captions in JSON format ******************************************************************************************/ function build$2(captions, options) { return JSON.stringify(captions, " ", 2); } /****************************************************************************************** * Detects a subtitle format from the content. ******************************************************************************************/ function detect$1(content) { if (typeof content === "string") { if (/^\[[\s\r\n]*\{[\s\S]*\}[\s\r\n]*\]$/g.test(content)) { /* [ { ... } ] */ return "json"; } } } /****************************************************************************************** * Export ******************************************************************************************/ var jsonFormatter = { name: FORMAT_NAME, detect: detect$1, parse: parse$2, build: build$2 }; var supportedFormats = { vtt: vttFormatter, lrc: lrcFormatter, smi: smiFormatter, ssa: ssaFormatter, ass: assFormatter, sub: subFormatter, srt: srtFormatter, sbv: sbvFormatter, json: jsonFormatter }; function clone(obj) { return JSON.parse(JSON.stringify(obj)); } /****************************************************************************************** * Gets a list of supported subtitle supportedFormats. ******************************************************************************************/ function list$4() { return Object.keys(supportedFormats); } /****************************************************************************************** * Detects a subtitle supportedFormats from the content. ******************************************************************************************/ function detect(content) { var formats = Object.keys(supportedFormats); for (var i = 0; i < formats.length; i++) { var formatName = formats[i]; var handler = supportedFormats[formatName]; if (handler === undefined) { continue; } if (typeof handler.detect != "function") { continue; } //Function 'detect' can return true or supportedFormats name var d = handler.detect(content); if (d === true) { //Logical true return formatName; } if (formatName === d) { //Format name return d; } } } /****************************************************************************************** * Parses a subtitle content. ******************************************************************************************/ function parse$1(content, options) { options = options || {}; var format = options.format || detect(content); if (!format || format.trim().length == 0) { throw new Error("Cannot determine subtitle supportedFormats!"); } var handler = supportedFormats[format]; if (handler === undefined) { throw new Error("Unsupported subtitle supportedFormats: " + format); } var func = handler.parse; if (typeof func != "function") { throw new Error("Subtitle supportedFormats does not support 'parse' op: " + format); } return func(content, options); } /****************************************************************************************** * Builds a subtitle content ******************************************************************************************/ function build$1(captions, options) { options = options || {}; var format = options.format || "srt"; if (!format || format.trim().length == 0) { throw new Error("Cannot determine subtitle supportedFormats!"); } var handler = supportedFormats[format]; if (typeof handler == "undefined") { throw new Error("Unsupported subtitle supportedFormats: " + format); } var func = handler.build; if (typeof func != "function") { throw new Error("Subtitle supportedFormats does not support 'build' op: " + format); } return func(captions, options); } /****************************************************************************************** * Converts subtitle supportedFormats ******************************************************************************************/ function convert(content, options) { if (typeof options == "string") { options = { to: options }; } options = options || {}; var opt = clone(options); delete opt.format; if (opt.from) { opt.format = opt.from; } var captions = parse$1(content, opt); if (opt.resync) { captions = resync(captions, opt.resync); } opt.format = opt.to || options.format; return build$1(captions, opt); } /****************************************************************************************** * Shifts the time of the captions. ******************************************************************************************/ function resync(captions, options) { options = options || {}; var func, ratio, frame, offset; if (typeof options == "function") { func = options; //User's function to handle time shift } else if (typeof options == "number") { offset = options; //Time shift (+/- offset) func = function func(a) { return [a[0] + offset, a[1] + offset]; }; } else if (_typeof(options) == "object") { offset = (options.offset || 0) * (options.frame ? options.fps || 25 : 1); ratio = options.ratio || 1.0; frame = options.frame; func = function func(a) { return [Math.round(a[0] * ratio + offset), Math.round(a[1] * ratio + offset)]; }; } else { throw new Error("Argument 'options' not defined!"); } var resynced = []; for (var i = 0; i < captions.length; i++) { var caption = clone(captions[i]); if (typeof caption.type === "undefined" || caption.type == "caption") { if (frame) { var shift = func([caption.frame.start, caption.frame.end]); if (shift && shift.length == 2) { caption.frame.start = shift[0]; caption.frame.end = shift[1]; caption.frame.count = caption.frame.end - caption.frame.start; } } else { var shift = func([caption.start, caption.end]); if (shift && shift.length == 2) { caption.start = shift[0]; caption.end = shift[1]; caption.duration = caption.end - caption.start; } } } resynced.push(caption); } return resynced; } var subsrt = { list: list$4, detect: detect, parse: parse$1, build: build$1, convert: convert, resync: resync }; /** * Получить время * @param {string} val - Время в формате "HH:MM:SS,mmm" или "HH:MM:SS.mmm" * @returns {number} */ function time$1(val) { var regex = /(\d+):(\d{2}):(\d{2})[.,](\d{3})/; var parts = regex.exec(val); if (parts === null) return 0; for (var i = 1; i < 5; i++) { parts[i] = parseInt(parts[i], 10); if (isNaN(parts[i])) parts[i] = 0; } //hours + minutes + seconds + ms return parts[1] * 3600000 + parts[2] * 60000 + parts[3] * 1000 + parts[4]; } /** * Парсить * @param {string} data - Данные субтитров в формате SRT или VTT * @param {boolean} ms - Использовать миллисекунды * @returns */ function parse(data, ms) { var type = subsrt.detect(data); if (type === 'vtt') return parseVTT(data, ms); if (type !== undefined && type !== 'srt') return parseVTT(convertToVTT(data), ms);else return parseSRT(data, ms); } /** * Парсить SRT * @param {string} data - Данные субтитров в формате SRT * @param {boolean} ms - Использовать миллисекунды * @returns {[{id:string, startTime:number, endTime:number, text:string}]} */ function parseSRT(data, ms) { var useMs = ms ? true : false; data = data.replace(/\r\n/g, '\n'); data = data.replace(/\r/g, '\n'); var regex = /(\d+)\n(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})/g; data = data.split(regex); data.shift(); var items = []; for (var i = 0; i < data.length; i += 4) { items.push({ id: data[i].trim(), startTime: useMs ? time$1(data[i + 1].trim()) : data[i + 1].trim(), endTime: useMs ? time$1(data[i + 2].trim()) : data[i + 2].trim(), text: data[i + 3].trim() }); } return items; } /** * Конвертировать в VTT * @param {string} data - Данные субтитров в формате SRT или VTT * @returns {string} - Данные субтитров в формате VTT */ function convertToVTT(data) { return subsrt.convert(data, { format: 'vtt', fps: 25 }); } /** * Парсить VTT * @param {string} data - Данные субтитров в формате VTT * @param {boolean} ms - Использовать миллисекунды * @returns {[{id:string, startTime:number, endTime:number, text:string}]} */ function parseVTT(data, ms) { var useMs = ms ? true : false; data = data.replace(/\r\n/g, '\n'); data = data.replace(/\r/g, '\n'); data = data.replace(/(\d{2}):(\d{2})\.(\d{3})[ \t]+-->[ \t]+(\d{2}):(\d{2})\.(\d{3})/g, '00:$1:$2.$3 --> 00:$4:$5.$6'); var regex = /(\n\n.+\n)?[ \t]*(\d{2}:\d{2}:\d{2}\.\d{3})[ \t]+-->[ \t]+(\d{2}:\d{2}:\d{2}\.\d{3})/g; data = data.split(regex); data.shift(); var items = []; for (var i = 0; i < data.length; i += 4) { items.push({ id: (data[i] || '').trim(), startTime: useMs ? time$1(data[i + 1].trim()) : data[i + 1].trim(), endTime: useMs ? time$1(data[i + 2].trim()) : data[i + 2].trim(), text: data[i + 3].trim() }); } return items; } /** * Кастомные субтитры * @class */ function CustomSubtitles() { var parsed; var network = new Request(); this.listener = Subscribe$2(); /** * Загрузить * @param {string} url */ this.load = function (url) { network.silent(url, function (data) { if (data) { parsed = parse(data, true); } }, false, false, { dataType: 'text' }); }; /** * Показать текст * @param {number} time_sec */ this.update = function (time_sec) { var time_ms = time_sec * 1000; time_ms -= parseInt(Storage.get('player_subs_shift_time', '0')) * 1000; if (parsed) { var text = ''; for (var i = 0; i < parsed.length; i++) { var sub = parsed[i]; if (time_ms > sub.startTime && time_ms < sub.endTime) { text = sub.text.replace("\n", '
    '); break; } } this.listener.send('subtitle', { text: text.trim() }); } }; /** * Уничтожить */ this.destroy = function () { network.clear(); network = null; this.listener = null; }; } var custom_subtitles; function init$I() { Player.listener.follow('ready', bind$2); Player.listener.follow('destroy', destroy$6); PlayerVideo.listener.follow('timeupdate', function (e) { if (custom_subtitles) custom_subtitles.update(e.current); }); // Video.listener.follow('destroy', function(e) { // if(custom_subtitles){ // custom_subtitles.destroy() // custom_subtitles = null // } // }) PanelSettings.listener.follow('subs', applySettings); } /** * Применяет к блоку субтитров пользовательские настройки */ function applySettings() { var stroke = Storage.field('subtitles_stroke'), backdrop = Storage.field('subtitles_backdrop'), size = Storage.field('subtitles_size'); Cue.render().removeClass('has--stroke has--backdrop size--normal size--large size--small'); Cue.render().addClass('size--' + size); if (stroke) Cue.render().addClass('has--stroke'); if (backdrop) Cue.render().addClass('has--backdrop'); } function bind$2(data) { // Субтитры из плеера Tizen PlayerVideo.video().addEventListener('subtitle', function (e) { //В srt существует тег {\anX}, где X - цифра от 1 до 9, Тег определяет нестандартное положение субтитра на экране. //Здесь удаляется тег из строки и обрабатывается положение 8 (субтитр вверху по центру). //{\an8} используется когда нужно, чтобы субтитр не перекрывал надписи в нижней части экрана или субтитры вшитые в видеоряд. Cue.render().removeClass('on-top'); var pos_tag = e.text.match(/^{\\an(\d)}/); if (pos_tag) { e.text = e.text.replace(/^{\\an(\d)}/, ''); if (pos_tag[1] && parseInt(pos_tag[1]) === 8) { Cue.render().addClass('on-top'); } } Cue.draw(e.text.trim()); }); applySettings(); if (data.subtitles) custom(data.subtitles); } /** * Установить собственные субтитры * @param {[{index:integer, label:string, url:string}]} list */ function custom(list) { if (!Arrays.isArray(list)) return console.log('Player', 'custom subtitles not array', list); var video = PlayerVideo.video(); if (custom_subtitles) custom_subtitles.destroy(); video.customSubs = Arrays.clone(list); console.log('Player', 'custom subtitles', list); custom_subtitles = new CustomSubtitles(); custom_subtitles.listener.follow('subtitle', function (e) { Cue.draw(e.text, e.style); }); var index = -1; video.customSubs.forEach(function (sub) { index++; if (typeof sub.index == 'undefined') sub.index = index; if (!sub.ready) { sub.ready = true; Object.defineProperty(sub, "mode", { set: function set(v) { if (v == 'showing') { custom_subtitles.load(sub.url); } }, get: function get() {} }); } }); video.customSubs.length > 0 && PlayerVideo.listener.send('subs', { subs: video.customSubs }); } function destroy$6() { Cue.draw(''); if (custom_subtitles) { custom_subtitles.destroy(); custom_subtitles = null; } } var Subtitles = { init: init$I, custom: custom, destroy: destroy$6 }; var NoiseEffect = /*#__PURE__*/function () { function NoiseEffect(name) { _classCallCheck(this, NoiseEffect); this.name = name; this.enabled = true; } _createClass(NoiseEffect, [{ key: "update", value: function update() {} }, { key: "draw", value: function draw() {} }, { key: "destroy", value: function destroy() {} }]); return NoiseEffect; }(); var NoiseBandsEffect = /*#__PURE__*/function (_NoiseEffect) { _inherits(NoiseBandsEffect, _NoiseEffect); var _super = _createSuper(NoiseBandsEffect); function NoiseBandsEffect() { var _options$count; var _this; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, NoiseBandsEffect); _this = _super.call(this, 'bands'); _this.count = (_options$count = options.count) !== null && _options$count !== void 0 ? _options$count : 10; _this.bands = []; return _this; } _createClass(NoiseBandsEffect, [{ key: "init", value: function init(noise) { this.noise = noise; this.reset(); } }, { key: "reset", value: function reset() { this.bands = []; for (var i = 0; i < this.count; i++) { this.bands.push(this.createBand()); } } }, { key: "createBand", value: function createBand() { var height = 5 + Math.random() * 35; return { y: Math.random() * this.noise.height, height: height, speed: (Math.random() - 0.5) * 1.2, strength: 0.25 + Math.random() * 1.1, life: 30 + Math.random() * 160 }; } }, { key: "update", value: function update() { for (var i = 0; i < this.bands.length; i++) { var band = this.bands[i]; band.y += band.speed; band.life--; if (band.life <= 0 || band.y > this.noise.height || band.y + band.height < 0) { this.bands[i] = this.createBand(); } } } }, { key: "getStrength", value: function getStrength(y) { var strength = 0; var _iterator = _createForOfIteratorHelper(this.bands), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var band = _step.value; if (y < band.y || y > band.y + band.height) { continue; } var center = band.y + band.height / 2; var distance = Math.abs(y - center); var fade = 1 - distance / (band.height / 2); strength += band.strength * Math.max(0, fade); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return Math.min(strength, 1.8); } }, { key: "destroy", value: function destroy() { this.bands = []; this.noise = null; } }]); return NoiseBandsEffect; }(NoiseEffect); var HorizontalSmearEffect = /*#__PURE__*/function (_NoiseEffect2) { _inherits(HorizontalSmearEffect, _NoiseEffect2); var _super2 = _createSuper(HorizontalSmearEffect); function HorizontalSmearEffect() { var _options$minCount, _options$maxCount; var _this2; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, HorizontalSmearEffect); _this2 = _super2.call(this, 'smear'); _this2.minCount = (_options$minCount = options.minCount) !== null && _options$minCount !== void 0 ? _options$minCount : 12; _this2.maxCount = (_options$maxCount = options.maxCount) !== null && _options$maxCount !== void 0 ? _options$maxCount : 35; return _this2; } _createClass(HorizontalSmearEffect, [{ key: "draw", value: function draw(noise) { var ctx = noise.ctx, canvas = noise.canvas, width = noise.width, height = noise.height; var count = this.minCount + Math.floor(Math.random() * (this.maxCount - this.minCount + 1)); for (var i = 0; i < count; i++) { var y = Math.floor(Math.random() * height); var sliceHeight = 1 + Math.floor(Math.random() * 5); var sourceX = Math.floor(Math.random() * width); var sourceWidth = 5 + Math.floor(Math.random() * Math.max(5, width - sourceX)); var destinationX = sourceX + Math.floor((Math.random() - 0.5) * 120); var destinationWidth = sourceWidth * (1.2 + Math.random() * 5); ctx.globalAlpha = 0.1 + Math.random() * 0.55; ctx.drawImage(canvas, sourceX, y, sourceWidth, sliceHeight, destinationX, y, destinationWidth, sliceHeight); } ctx.globalAlpha = 1; } }]); return HorizontalSmearEffect; }(NoiseEffect); var DisplacedBandsEffect = /*#__PURE__*/function (_NoiseEffect3) { _inherits(DisplacedBandsEffect, _NoiseEffect3); var _super3 = _createSuper(DisplacedBandsEffect); function DisplacedBandsEffect() { var _options$minCount2, _options$maxCount2, _options$maxOffset; var _this3; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, DisplacedBandsEffect); _this3 = _super3.call(this, 'displacement'); _this3.minCount = (_options$minCount2 = options.minCount) !== null && _options$minCount2 !== void 0 ? _options$minCount2 : 3; _this3.maxCount = (_options$maxCount2 = options.maxCount) !== null && _options$maxCount2 !== void 0 ? _options$maxCount2 : 9; _this3.maxOffset = (_options$maxOffset = options.maxOffset) !== null && _options$maxOffset !== void 0 ? _options$maxOffset : 90; return _this3; } _createClass(DisplacedBandsEffect, [{ key: "draw", value: function draw(noise) { var ctx = noise.ctx, canvas = noise.canvas, width = noise.width, height = noise.height; var count = this.minCount + Math.floor(Math.random() * (this.maxCount - this.minCount + 1)); for (var i = 0; i < count; i++) { var y = Math.floor(Math.random() * height); var bandHeight = 2 + Math.floor(Math.random() * 20); var offset = Math.floor((Math.random() - 0.5) * this.maxOffset * 2); ctx.globalAlpha = 0.3 + Math.random() * 0.65; ctx.drawImage(canvas, 0, y, width, bandHeight, offset, y, width, bandHeight); if (Math.random() < 0.55) { var color = Math.random(); if (color < 0.33) { ctx.fillStyle = 'rgba(0, 180, 255, 0.22)'; } else if (color < 0.66) { ctx.fillStyle = 'rgba(0, 255, 170, 0.18)'; } else { ctx.fillStyle = 'rgba(255, 30, 90, 0.16)'; } ctx.fillRect(0, y, width, 1 + Math.floor(Math.random() * 2)); } } ctx.globalAlpha = 1; } }]); return DisplacedBandsEffect; }(NoiseEffect); var RandomLinesEffect = /*#__PURE__*/function (_NoiseEffect4) { _inherits(RandomLinesEffect, _NoiseEffect4); var _super4 = _createSuper(RandomLinesEffect); function RandomLinesEffect() { var _options$minCount3, _options$maxCount3; var _this4; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, RandomLinesEffect); _this4 = _super4.call(this, 'lines'); _this4.minCount = (_options$minCount3 = options.minCount) !== null && _options$minCount3 !== void 0 ? _options$minCount3 : 8; _this4.maxCount = (_options$maxCount3 = options.maxCount) !== null && _options$maxCount3 !== void 0 ? _options$maxCount3 : 22; return _this4; } _createClass(RandomLinesEffect, [{ key: "draw", value: function draw(noise) { var ctx = noise.ctx, width = noise.width, height = noise.height; var count = this.minCount + Math.floor(Math.random() * (this.maxCount - this.minCount + 1)); for (var i = 0; i < count; i++) { var x = Math.floor(Math.random() * width); var y = Math.floor(Math.random() * height); var lineWidth = 20 + Math.random() * Math.max(20, width - x); if (Math.random() < 0.65) { var r = Math.floor(Math.random() * 90); var g = 90 + Math.floor(Math.random() * 165); var b = 100 + Math.floor(Math.random() * 155); var alpha = 0.06 + Math.random() * 0.3; ctx.fillStyle = "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")"); } else { ctx.fillStyle = "rgba(255,255,255,".concat(0.04 + Math.random() * 0.25, ")"); } ctx.fillRect(x, y, lineWidth, Math.random() < 0.85 ? 1 : 2); } } }]); return RandomLinesEffect; }(NoiseEffect); var FrameJumpEffect = /*#__PURE__*/function (_NoiseEffect5) { _inherits(FrameJumpEffect, _NoiseEffect5); var _super5 = _createSuper(FrameJumpEffect); function FrameJumpEffect() { var _options$chance, _options$maxX, _options$maxY; var _this5; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, FrameJumpEffect); _this5 = _super5.call(this, 'jump'); _this5.chance = (_options$chance = options.chance) !== null && _options$chance !== void 0 ? _options$chance : 0.07; _this5.maxX = (_options$maxX = options.maxX) !== null && _options$maxX !== void 0 ? _options$maxX : 14; _this5.maxY = (_options$maxY = options.maxY) !== null && _options$maxY !== void 0 ? _options$maxY : 5; return _this5; } _createClass(FrameJumpEffect, [{ key: "draw", value: function draw(noise) { if (Math.random() > this.chance) { return; } var ctx = noise.ctx, canvas = noise.canvas; var offsetX = Math.floor((Math.random() - 0.5) * this.maxX * 2); var offsetY = Math.floor((Math.random() - 0.5) * this.maxY * 2); ctx.globalAlpha = 0.75; ctx.drawImage(canvas, offsetX, offsetY); ctx.globalAlpha = 1; } }]); return FrameJumpEffect; }(NoiseEffect); var ScanlinesEffect = /*#__PURE__*/function (_NoiseEffect6) { _inherits(ScanlinesEffect, _NoiseEffect6); var _super6 = _createSuper(ScanlinesEffect); function ScanlinesEffect() { var _options$step, _options$alpha; var _this6; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, ScanlinesEffect); _this6 = _super6.call(this, 'scanlines'); _this6.step = (_options$step = options.step) !== null && _options$step !== void 0 ? _options$step : 3; _this6.alpha = (_options$alpha = options.alpha) !== null && _options$alpha !== void 0 ? _options$alpha : 0.2; return _this6; } _createClass(ScanlinesEffect, [{ key: "draw", value: function draw(noise) { var ctx = noise.ctx, width = noise.width, height = noise.height; ctx.fillStyle = "rgba(0,0,0,".concat(this.alpha, ")"); for (var y = 0; y < height; y += this.step) { ctx.fillRect(0, y, width, 1); } } }]); return ScanlinesEffect; }(NoiseEffect); var VignetteEffect = /*#__PURE__*/function (_NoiseEffect7) { _inherits(VignetteEffect, _NoiseEffect7); var _super7 = _createSuper(VignetteEffect); function VignetteEffect() { var _options$strength; var _this7; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, VignetteEffect); _this7 = _super7.call(this, 'vignette'); _this7.strength = (_options$strength = options.strength) !== null && _options$strength !== void 0 ? _options$strength : 0.7; return _this7; } _createClass(VignetteEffect, [{ key: "draw", value: function draw(noise) { var ctx = noise.ctx, width = noise.width, height = noise.height; var gradient = ctx.createRadialGradient(width / 2, height / 2, height * 0.12, width / 2, height / 2, width * 0.72); gradient.addColorStop(0, 'rgba(0,0,0,0)'); gradient.addColorStop(0.65, 'rgba(0,0,0,0.05)'); gradient.addColorStop(1, "rgba(0,0,0,".concat(this.strength, ")")); ctx.fillStyle = gradient; ctx.fillRect(0, 0, width, height); } }]); return VignetteEffect; }(NoiseEffect); var FlickerEffect = /*#__PURE__*/function (_NoiseEffect8) { _inherits(FlickerEffect, _NoiseEffect8); var _super8 = _createSuper(FlickerEffect); function FlickerEffect() { var _options$chance2; var _this8; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, FlickerEffect); _this8 = _super8.call(this, 'flicker'); _this8.chance = (_options$chance2 = options.chance) !== null && _options$chance2 !== void 0 ? _options$chance2 : 0.2; return _this8; } _createClass(FlickerEffect, [{ key: "draw", value: function draw(noise) { if (Math.random() > this.chance) { return; } var ctx = noise.ctx, width = noise.width, height = noise.height; if (Math.random() < 0.5) { ctx.fillStyle = "rgba(255,255,255,".concat(Math.random() * 0.035, ")"); } else { ctx.fillStyle = "rgba(0,0,0,".concat(Math.random() * 0.1, ")"); } ctx.fillRect(0, 0, width, height); } }]); return FlickerEffect; }(NoiseEffect); var Noise = /*#__PURE__*/function () { function Noise(canvas) { var _options$width, _options$height, _options$fps, _options$baseBrightne, _options$bandBrightne, _options$trailAlpha, _options$effects; var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, Noise); if (!(canvas instanceof HTMLCanvasElement)) { throw new TypeError('Noise: первым аргументом должен быть canvas'); } this.canvas = canvas; this.ctx = canvas.getContext('2d', { alpha: false }); if (!this.ctx) { throw new Error('Noise: Canvas 2D не поддерживается'); } this.width = (_options$width = options.width) !== null && _options$width !== void 0 ? _options$width : 480; this.height = (_options$height = options.height) !== null && _options$height !== void 0 ? _options$height : 270; this.fps = (_options$fps = options.fps) !== null && _options$fps !== void 0 ? _options$fps : 25; this.baseBrightness = (_options$baseBrightne = options.baseBrightness) !== null && _options$baseBrightne !== void 0 ? _options$baseBrightne : 16; this.bandBrightness = (_options$bandBrightne = options.bandBrightness) !== null && _options$bandBrightne !== void 0 ? _options$bandBrightne : 145; this.trailAlpha = (_options$trailAlpha = options.trailAlpha) !== null && _options$trailAlpha !== void 0 ? _options$trailAlpha : 0.84; this.running = false; this.destroyed = false; this.animationId = null; this.lastFrameTime = 0; this._renderBound = this._render.bind(this); this.canvas.width = this.width; this.canvas.height = this.height; this.ctx.imageSmoothingEnabled = false; this._createBuffer(); this.effects = (_options$effects = options.effects) !== null && _options$effects !== void 0 ? _options$effects : [new NoiseBandsEffect(), new HorizontalSmearEffect(), new DisplacedBandsEffect(), new RandomLinesEffect(), new FrameJumpEffect(), new ScanlinesEffect(), new VignetteEffect(), new FlickerEffect()]; var _iterator2 = _createForOfIteratorHelper(this.effects), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var effect = _step2.value; if (typeof effect.init === 'function') { effect.init(this); } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } this.start(); } _createClass(Noise, [{ key: "_createBuffer", value: function _createBuffer() { this.noiseCanvas = document.createElement('canvas'); this.noiseCanvas.width = this.width; this.noiseCanvas.height = this.height; this.noiseCtx = this.noiseCanvas.getContext('2d', { alpha: false }); if (!this.noiseCtx) { throw new Error('Noise: не удалось создать внутренний canvas'); } this.noiseImage = this.noiseCtx.createImageData(this.width, this.height); this.pixels = this.noiseImage.data; } }, { key: "getEffect", value: function getEffect(name) { var _this$effects$find; return (_this$effects$find = this.effects.find(function (effect) { return effect.name === name; })) !== null && _this$effects$find !== void 0 ? _this$effects$find : null; } }, { key: "enable", value: function enable(name) { var effect = this.getEffect(name); if (effect) { effect.enabled = true; } return this; } }, { key: "disable", value: function disable(name) { var effect = this.getEffect(name); if (effect) { effect.enabled = false; } return this; } }, { key: "toggle", value: function toggle(name, enabled) { var effect = this.getEffect(name); if (effect) { effect.enabled = enabled === undefined ? !effect.enabled : Boolean(enabled); } return this; } }, { key: "addEffect", value: function addEffect(effect) { if (!effect || typeof effect.draw !== 'function') { throw new TypeError('Noise.addEffect: передан некорректный эффект'); } if (typeof effect.init === 'function') { effect.init(this); } this.effects.push(effect); return this; } }, { key: "removeEffect", value: function removeEffect(name) { var index = this.effects.findIndex(function (effect) { return effect.name === name; }); if (index === -1) { return this; } var _this$effects$splice = this.effects.splice(index, 1), _this$effects$splice2 = _slicedToArray(_this$effects$splice, 1), effect = _this$effects$splice2[0]; if (typeof effect.destroy === 'function') { effect.destroy(); } return this; } }, { key: "start", value: function start() { if (this.destroyed || this.running) { return this; } this.running = true; this.lastFrameTime = 0; this.animationId = requestAnimationFrame(this._renderBound); return this; } }, { key: "stop", value: function stop() { this.running = false; if (this.animationId !== null) { cancelAnimationFrame(this.animationId); this.animationId = null; } return this; } }, { key: "resize", value: function resize(width, height) { if (this.destroyed) { return this; } if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) { throw new TypeError('Noise.resize: некорректный размер'); } this.width = Math.floor(width); this.height = Math.floor(height); this.canvas.width = this.width; this.canvas.height = this.height; this.ctx.imageSmoothingEnabled = false; this._destroyBuffer(); this._createBuffer(); var _iterator3 = _createForOfIteratorHelper(this.effects), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var effect = _step3.value; if (typeof effect.reset === 'function') { effect.reset(); } } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } return this; } }, { key: "_getBandsEffect", value: function _getBandsEffect() { var effect = this.getEffect('bands'); return effect !== null && effect !== void 0 && effect.enabled ? effect : null; } }, { key: "_generateNoise", value: function _generateNoise() { var bandsEffect = this._getBandsEffect(); var index = 0; for (var y = 0; y < this.height; y++) { var bandStrength = bandsEffect ? bandsEffect.getStrength(y) : 0; var rowBrightness = Math.random() < 0.14 ? Math.random() * 0.9 : Math.random() * 0.12; for (var x = 0; x < this.width; x++) { var intensity = Math.random() * this.baseBrightness + bandStrength * Math.random() * this.bandBrightness + rowBrightness * 65; var brightPixelChance = 0.025 + bandStrength * 0.11; if (Math.random() < brightPixelChance) { intensity += 60 + Math.random() * 195; } var colorMode = Math.random(); var r = void 0; var g = void 0; var b = void 0; if (colorMode < 0.24) { r = intensity * (0.7 + Math.random() * 0.8); g = intensity * (0.15 + Math.random() * 0.5); b = intensity * (0.05 + Math.random() * 0.35); } else if (colorMode < 0.48) { r = intensity * (0.05 + Math.random() * 0.35); g = intensity * (0.55 + Math.random() * 0.8); b = intensity * (0.45 + Math.random() * 0.8); } else if (colorMode < 0.72) { r = intensity * (0.1 + Math.random() * 0.4); g = intensity * (0.1 + Math.random() * 0.5); b = intensity * (0.65 + Math.random() * 0.9); } else { var gray = intensity * (0.55 + Math.random() * 0.55); r = gray; g = gray; b = gray; } this.pixels[index] = Math.min(255, r); this.pixels[index + 1] = Math.min(255, g); this.pixels[index + 2] = Math.min(255, b); this.pixels[index + 3] = 255; index += 4; } } this.noiseCtx.putImageData(this.noiseImage, 0, 0); } }, { key: "_render", value: function _render(time) { if (!this.running || this.destroyed) { return; } this.animationId = requestAnimationFrame(this._renderBound); var frameDuration = 1000 / this.fps; if (time - this.lastFrameTime < frameDuration) { return; } this.lastFrameTime = time - (time - this.lastFrameTime) % frameDuration; var _iterator4 = _createForOfIteratorHelper(this.effects), _step4; try { for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { var effect = _step4.value; if (effect.enabled && typeof effect.update === 'function') { effect.update(this); } } } catch (err) { _iterator4.e(err); } finally { _iterator4.f(); } this._generateNoise(); this.ctx.globalAlpha = this.trailAlpha; this.ctx.drawImage(this.noiseCanvas, 0, 0, this.width, this.height); this.ctx.globalAlpha = 1; var _iterator5 = _createForOfIteratorHelper(this.effects), _step5; try { for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { var _effect = _step5.value; if (_effect.enabled && _effect.name !== 'bands' && typeof _effect.draw === 'function') { _effect.draw(this); } } } catch (err) { _iterator5.e(err); } finally { _iterator5.f(); } } }, { key: "_destroyBuffer", value: function _destroyBuffer() { if (this.noiseCanvas) { this.noiseCanvas.width = 0; this.noiseCanvas.height = 0; } this.noiseImage = null; this.pixels = null; this.noiseCtx = null; this.noiseCanvas = null; } }, { key: "destroy", value: function destroy() { if (this.destroyed) { return; } this.stop(); var _iterator6 = _createForOfIteratorHelper(this.effects), _step6; try { for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { var effect = _step6.value; if (typeof effect.destroy === 'function') { effect.destroy(); } } } catch (err) { _iterator6.e(err); } finally { _iterator6.f(); } this.effects.length = 0; if (this.ctx && this.canvas) { this.ctx.globalAlpha = 1; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } this._destroyBuffer(); this.ctx = null; this.canvas = null; this._renderBound = null; this.destroyed = true; } }]); return Noise; }(); var noise; function init$H() { PlayerVideo.listener.follow('error', function (e) { if (e.fatal) { destroy$5(); create$7(e.error); } }); // Если видео загрузилось, то шум убираем PlayerVideo.listener.follow('loadeddata', function () { console.log('Player', 'video loadeddata'); destroy$5(); }); PlayerVideo.listener.follow('destroy', destroy$5); } function create$7(text) { noise = {}; noise.html = $("
    \n \n
    \n
    \n
    ".concat(text, "
    \n
    \n
    ")); PlayerVideo.render().append(noise.html); PlayerVideo.render().toggleClass('player-video--error', true); noise["class"] = new Noise(noise.html.find('canvas')[0], { width: 320, height: 180, fps: 25, baseBrightness: 14, bandBrightness: 30, trailAlpha: 0.4 }); noise["class"].disable('displacement'); noise["class"].disable('jump'); noise["class"].disable('scanlines'); noise["class"].disable('vignette'); noise["class"].disable('flicker'); noise["class"].disable('smear'); } function destroy$5() { if (noise) { noise["class"].destroy(); noise.html.remove(); noise = null; } PlayerVideo.render().toggleClass('player-video--error', false); } var Error$1 = { init: init$H, destroy: destroy$5 }; var html$c; var listener$7 = Subscribe$2(); var callback$2; var work = false; var launch_player; var wait_for_loading_url = false; var wait_loading = false; var is_opened = false; var viewing = { time: 0, difference: 0, current: 0 }; /** * Подписываемся на события */ function init$G() { PlayerPanel.init(); PlayerVideo.init(); PlayerInfo.init(); PlayerFooter.init(); PlayerIPTV.init(); Agent.init(); PlayerChat.init(); Charts.init(); Skip.init(); Timeline.init(); PlayerPlaylist.init(); Segments$1.init(); Disclaimer.init(); Subtitles.init(); Error$1.init(); html$c = Template.get('player'); html$c.append(PlayerVideo.render()); html$c.append(PlayerPanel.render()); html$c.append(PlayerInfo.render()); html$c.append(PlayerFooter.render()); html$c.append(PlayerChat.render()); html$c.append(Skip.render()); var timer_hide_cursor; /** Скрываем курсор через 3 секунды */ html$c.on('mousemove', function () { if (Storage.field('navigation_type') == 'mouse' && !Utils$1.isTouchDevice()) PlayerPanel.mousemove(); html$c.css('cursor', 'auto'); clearTimeout(timer_hide_cursor); timer_hide_cursor = setTimeout(function () { html$c.css('cursor', 'none'); }, 3000); }); /** Обновляем таймкод */ PlayerVideo.listener.follow('timeupdate', function (e) { Screensaver$2.resetTimer(); viewing.difference = e.current - viewing.current; viewing.current = e.current; if (viewing.difference > 0 && viewing.difference < 3) viewing.time += viewing.difference; }); /** Дорожки полученые из видео */ PlayerVideo.listener.follow('tracks', function (e) { if (!work.voiceovers) PlayerPanel.setTracks(e.tracks); }); /** Ошибка при попытки возпроизвести */ PlayerVideo.listener.follow('error', function (e) { // Если есть резервная ссылка, то пробуем её if (e.fatal && work.url_reserve) { PlayerVideo.destroy(true); PlayerVideo.url(work.url_reserve, true); Timeline.resetContinue(); delete work.url_reserve; } // Если есть резервная ссылка и есть обработчик ошибки, то пробуем её через обработчик if (e.fatal && work.error) work.error(work, function (reserve_url) { PlayerVideo.destroy(true); PlayerVideo.url(reserve_url, true); Timeline.resetContinue(); }); }); /** Видео загружено, проверяем нужно ли скрывать кнопку перемотки */ PlayerVideo.listener.follow('loadeddata', function () { if (PlayerVideo.video().duration < 60 * 3 && work.need_check_live_stream) { PlayerPanel.hideRewind(); } }); /** Состояние панели, скрыта или нет */ PlayerPanel.listener.follow('visible', function (e) { html$c.toggleClass('player--panel-visible', e.status); }); /** К концу видео */ PlayerPanel.listener.follow('to_end', function (e) { if (PlayerPlaylist.canNext()) { PlayerVideo.pause(); Timeline.setEnd(); PlayerPlaylist.next(); } else { PlayerVideo.to(-1); } }); /** Переключили качеcтво видео */ PlayerPanel.listener.follow('quality', function (e) { PlayerVideo.destroy(true); work.quality_switched = e.name; work.url = e.url; PlayerVideo.url(Torserver.toPlayUrl(e.url), true); Timeline.resetContinue(); }); /** Переключили поток */ PlayerPanel.listener.follow('flow', function (e) { PlayerVideo.destroy(true); work.flow_switched = e.url; work.url = e.url; PlayerVideo.url(Torserver.toPlayUrl(e.url), true); Timeline.resetContinue(); }); /** Нажали на кнопку (отправить) */ PlayerPanel.listener.follow('share', function (e) { Broadcast.open({ type: 'play', object: { player: work, playlist: PlayerPlaylist.get() } }); }); /** Событие на переключение серии */ PlayerPlaylist.listener.follow('select', function (e) { var type = _typeof(e.item.url); var call = function call() { var params = PlayerVideo.saveParams(); var keepPip = document.pictureInPictureElement === PlayerVideo.video() && typeof e.item.url == 'string' && !PlayerVideo.verifyTube(e.item.url); // Нужно текущий плейлист сохранить, чтобы после destroy в плеере остался правильный плейлист var playlist = PlayerPlaylist.get(); if (keepPip) PlayerVideo.pause(); destroy$4(keepPip); // Помечаем как продолжение воспроизведения e.item.continue_play = true; play$1(e.item); PlayerVideo.setParams(params); PlayerPlaylist.set(playlist); if (e.item.callback) e.item.callback(); }; if (type == 'string') call();else if (type == 'function' && !wait_for_loading_url) { PlayerInfo.loading(); wait_for_loading_url = true; e.item.url(call); } }); PlayerIPTV.listener.follow('play', function (data) { PlayerIPTV.locked(data.channel, function () { PlayerVideo.destroy(); PlayerVideo.url(data.channel.url); Controller.toggle('player_tv'); }); }); listener$7.follow('start', function (data) { Background.theme('black'); is_opened = true; // Упрощенный интерфейс для IPTV html$c.toggleClass('tv', Boolean(data.tv)); // Интерфейс для YouTube html$c.toggleClass('youtube', Boolean(data.url.indexOf('youtube.com') >= 0)); Storage.set('player_subs_shift_time', '0'); if (!html$c.parent().length) $('body').append(html$c); }); listener$7.follow('ready', function (data) { PlayerVideo.size(Storage.get('player_size', 'default')); PlayerVideo.speed(Storage.get('player_speed', 'default')); }); } /** * Переключить контроллер на плеер */ function toggle$5() { Controller.add('player', { invisible: true, toggle: function toggle() { PlayerPanel.hide(); }, up: function up() { PlayerPanel.toggle(); }, down: function down() { PlayerPanel.toggle(); }, right: function right() { if (PlayerIPTV.playning()) PlayerPanel.toggle();else PlayerVideo.rewind(true); }, left: function left() { if (PlayerIPTV.playning()) PlayerPanel.toggle();else PlayerVideo.rewind(false); }, enter: function enter() { if (PlayerIPTV.playning()) PlayerPanel.toggle();else PlayerVideo.playpause(); }, playpause: function playpause() { if (!PlayerIPTV.playning()) PlayerVideo.playpause(); }, play: function play() { if (!PlayerIPTV.playning()) PlayerVideo.play(); }, pause: function pause() { if (!PlayerIPTV.playning()) PlayerVideo.pause(); }, rewindForward: function rewindForward() { if (!PlayerIPTV.playning()) PlayerVideo.rewind(true); }, rewindBack: function rewindBack() { if (!PlayerIPTV.playning()) PlayerVideo.rewind(false); }, stop: backward$1, back: backward$1 }); Controller.add('player-loading', { invisible: true, toggle: function toggle() { Controller.clear(); PlayerPanel.show(); }, back: backward$1 }); Controller.toggle('player'); } /** * Закрыть плеер * @doc * @name close * @alias Player */ function backward$1() { launch_player = ''; destroy$4(); if (callback$2) callback$2();else Controller.toggle('content'); callback$2 = false; } /** * Запустить webos плеер * @param {Object} params */ function runWebOSPlayer(params) { webOS.service.request("luna://com.webos.applicationManager", { method: "launch", parameters: { "id": params.need, "params": { "payload": [{ "fullPath": params.url, "artist": "", "subtitle": "", "dlnaInfo": { "flagVal": 4096, "cleartextSize": "-1", "contentLength": "-1", "opVal": 1, "protocolInfo": "http-get:*:video/x-matroska:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000", "duration": 0 }, "mediaType": "VIDEO", "thumbnail": "", "deviceType": "DMR", "album": "", "fileName": params.name, "lastPlayPosition": params.position }] } }, onSuccess: function onSuccess() { console.log('Player', 'The app is launched'); }, onFailure: function onFailure(inError) { console.log('Player', "Failed to launch the app (" + params.need + "): ", "[" + inError.errorCode + "]: " + inError.errorText); if (params.need == 'com.webos.app.photovideo') { params.need = 'com.webos.app.smartshare'; runWebOSPlayer(params); } else if (params.need == 'com.webos.app.smartshare') { params.need = 'com.webos.app.mediadiscovery'; runWebOSPlayer(params); } } }); } /** * Получить ссылку на внешний плеер */ function externalPlayer(player_need, data, players, infuseCallbacks) { var player = Storage.field(player_need); var url = encodeURIComponent(Torserver.toPlayUrl(data.url)); var _url = encodeURI(Torserver.toPlayUrl(data.url)); var furl = Torserver.toPlayUrl(data.url); var playlist = data.playlist ? encodeURIComponent(JSON.stringify(data.playlist)) : ''; var segments = data.segments ? encodeURIComponent(JSON.stringify(data.segments)) : ''; for (var p in players) { players[p] = players[p].replace('${url}', url).replace('${_url}', _url).replace('${furl}', furl).replace('${playlist}', playlist).replace('${segments}', segments); } // Infuse: save_and_play, readable filenames, season playlist if (player == 'infuse') { InfusePlayer.normalizePlayData(data); var customUrl = null; listener$7.send('infuse_build_url', { data: data, callbacks: infuseCallbacks, setUrl: function setUrl(url) { customUrl = url; } }); if (customUrl) return customUrl; return InfusePlayer.resolveUrl(data, infuseCallbacks) || players.infuse; } return players[player]; } function prepareInfuseLaunch(data, player_need, callback, onCancel) { if (Storage.field(player_need) !== 'infuse') return callback(); // Торрент + Infuse: всегда play? с position if (InfusePlayer.isTorrentStream(data)) { data.infuse_mode = 'play'; return callback(); } var setting = Storage.field('infuse_launch_mode') || 'play'; if (setting !== 'ask') { data.infuse_mode = setting; return callback(); } delete data.infuse_mode; var enabled = Controller.enabled(); Select.show({ title: Lang.translate('title_action_infuse'), items: [{ title: Lang.translate('settings_infuse_launch_save_and_play'), mode: 'save_and_play' }, { title: Lang.translate('settings_infuse_launch_play'), mode: 'play' }], onSelect: function onSelect(item) { Controller.toggle(enabled.name); data.infuse_mode = item.mode; callback(); delete data.infuse_mode; }, onBack: function onBack() { Controller.toggle(enabled.name); if (onCancel) onCancel(); } }); } function launchExternalPlayer(data, player_need, players, infuseCallbacks, onFallback) { var launch = function launch(external_url) { if (!external_url) return onFallback ? onFallback() : null; Preroll.show(data, function () { listener$7.send('external', data); window.location.assign(external_url); }); }; prepareInfuseLaunch(data, player_need, function () { launch(externalPlayer(player_need, data, players, infuseCallbacks)); }, function () { listener$7.send('destroy', {}); }); } /** * Запустить нужный плеер в зависимости от условий */ function start$6(data, need, inner) { var player_need = 'player' + (need ? '_' + need : ''); var launchInner = function launchInner() { if (Disclaimer.needs(player_need, launch_player)) Disclaimer.show(inner);else inner(); }; if (data.launch_player) launch_player = data.launch_player; if (launch_player == 'lampa' || launch_player == 'inner' || PlayerVideo.verifyTube(data.url)) launchInner();else if (Platform.is('apple')) { launchExternalPlayer(data, player_need, { vlc: 'vlc://${furl}', nplayer: 'nplayer-${furl}', infuse: 'infuse://x-callback-url/play?url=${url}', senplayer: 'senplayer://x-callback-url/play?url=${url}', vidhub: 'open-vidhub://x-callback-url/open?&url=${url}', svplayer: 'svplayer://x-callback-url/stream?url=${url}', tracyplayer: 'tracy://open?url=${url}' }, null, function () { if (Storage.field(player_need) == 'ios') { html$c.addClass('player--ios'); launchInner(); } else launchInner(); }); } else if (Platform.macOS()) { launchExternalPlayer(data, player_need, { mpv: 'mpv://${_url}', iina: 'iina://weblink?url=${url}', nplayer: 'nplayer-${_url}', infuse: 'infuse://x-callback-url/play?url=${url}', senplayer: 'senplayer://x-callback-url/play?url=${url}' }, null, launchInner); } else if (Platform.is('apple_tv')) { var _Storage$field; var apple_tv_client = (_Storage$field = Storage.field('apple_tv_client')) !== null && _Storage$field !== void 0 ? _Storage$field : 'lampa'; launchExternalPlayer(data, player_need, { vlc: 'vlc-x-callback://x-callback-url/stream?url=${url}', infuse: "infuse://x-callback-url/play?x-success=".concat(apple_tv_client, "://infuseDidFinish&x-error=").concat(apple_tv_client, "://infuseDidFail&url=${url}&playlist=${playlist}"), senplayer: 'SenPlayer://x-callback-url/play?url=${url}', vidhub: 'open-vidhub://x-callback-url/open?url=${url}', svplayer: 'svplayer://x-callback-url/stream?url=${url}', tracyplayer: 'tracy://open?url=${url}', tvospro: 'lampa://video?player=tvospro&src=${url}&playlist=${playlist}&segments=${segments}', tvos: 'lampa://video?player=tvos&src=${url}&playlist=${playlist}&segments=${segments}', tvosl: 'lampa://video?player=tvosav&src=${url}&playlist=${playlist}&segments=${segments}', tvosSelect: 'lampa://video?player=lists&src=${url}&playlist=${playlist}&segments=${segments}' }, null, launchInner); } else if (Platform.is('webos') && (Storage.field(player_need) == 'webos' || launch_player == 'webos')) { Preroll.show(data, function () { runWebOSPlayer({ need: 'com.webos.app.photovideo', url: Torserver.toPlayUrl(data.url), name: data.path || data.title, position: data.timeline ? data.timeline.time || -1 : -1 }); listener$7.send('external', data); }); } else if (Platform.is('android') && (Storage.field(player_need) == 'android' || launch_player == 'android' || data.torrent_hash && !Torserver.gstWork())) { data.url = Torserver.toPlayUrl(data.url); data.title = Utils$1.clearHtmlTags(data.title || '').trim(); if (data.playlist && Array.isArray(data.playlist)) { data.playlist = data.playlist.filter(function (p) { return typeof p.url == 'string'; }); data.playlist.forEach(function (a) { a.url = Torserver.toPlayUrl(a.url); a.title = Utils$1.clearHtmlTags(a.title || '').trim(); }); } Preroll.show(data, function () { data.position = data.timeline ? data.timeline.time || -1 : -1; Android.openPlayer(data.url, data); listener$7.send('external', data); }); } else if (Platform.desktop() && Storage.field(player_need) == 'other') { var path = Storage.field('player_nw_path'); var supportedTypes = Object.values(ExternalPlayer.PLAYER_TYPES); var detectedType = supportedTypes.find(function (type) { return path.toLowerCase().indexOf(type) !== -1; }); Preroll.show(data, function () { var url = Torserver.toPlayUrl(data.url); if (detectedType) { ExternalPlayer.openPlayer(url, data, { type: detectedType, fullscreen: Storage.field('player_external_fullscreen') }); } else { var file = require('fs'); if (file.existsSync(path)) { var spawn = require('child_process').spawn; spawn(path, [encodeURI(url)]); } else { Noty.show(Lang.translate('player_not_found') + ': ' + path); } } listener$7.send('external', data); }); } else launchInner(); if (data.launch_player) delete data.launch_player; } /** * Получить URL по качеству видео * @doc * @name getUrlQuality * @alias Player * @param {object} quality JSON({"480p": "http://example/video.mp4", "720p": {"url": "http://example/video.mp4", "label": "HD"}, "1080p": {"label": "FHD", "call": "{function} - вызвать при выборе"}}) * @param {boolean} set_better установить лучшее качество, если нет дефолтного * @returns {string} URL */ function getUrlQuality(quality) { var set_better = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; if (_typeof(quality) !== 'object') return ''; var url = ''; for (var q in quality) { var qa = quality[q]; var qu = _typeof(qa) == 'object' ? qa.url : typeof qa == 'string' ? qa : ''; if (parseInt(q) == Storage.field('video_quality_default') && qu) return qu; } if (!url && set_better) { var sort_quality = Arrays.getKeys(quality); sort_quality.sort(function (a, b) { return parseInt(b) - parseInt(a); }); sort_quality.forEach(function (q) { var qa = quality[q]; var qu = _typeof(qa) == 'object' ? qa.url : typeof qa == 'string' ? qa : ''; if (qu && !url) url = qu; }); } return url; } /** * Запустить плеер * @doc * @name play * @alias Player * @param {object} data JSON({"url": "http://example/video.mp4", "quality": {"1080p": "http://example/video.mp4"}, "title": "Video title", "translate": [{"name": "Перевод", "language": "ru", "extra": {"channels": 2}}], "subtitles": [{"url": "http://example/subs.srt", "label": "RU Force"}], "card": "{object} - TMDB Card", "timeline": "{object} - Lampa.Timeline.view", "iptv": "{boolean} - запустить IPTV плеер", "tv": "{boolean} - имитация IPTV", "torrent_hash": "{string}", "playlist": [{"title":"{string} - Серия 1", "url":"{string} - http://example/video.mp4"}]}) */ function play$1(data) { var run = true; listener$7.send('create', { data: data, abort: function abort() { return run = false; } }); if (!run) return console.log('Player', 'play aborted by callback'); console.log('Player', 'start play'); console.log('Player', 'item url', data.url); console.log('Player', 'play data', data); // Если это торрент, то увеличиваем таймаут для HLS манифеста, чтобы не было проблем с буферизацией if (data.torrent_hash && Torserver.gstWork()) data.hls_manifest_timeout = 60000; if (data.ffprobe && !Arrays.isArray(data.translate)) { var tracks = tracksFromFfprobe(data.ffprobe); if (tracks.length) { if (!data.translate || _typeof(data.translate) !== 'object') data.translate = {}; if (!data.translate.tracks) data.translate.tracks = tracks; } } if (data.quality) { // Если качество одно, то удаляем объект качества, чтобы не показывать панель выбора качества if (Arrays.getKeys(data.quality).length == 1) delete data.quality;else { var quality_url = getUrlQuality(data.quality, false); if (quality_url) { // Меняем ссылку на видео на выбранное качество, чтобы плеер сразу начал воспроизведение с нужного качества data.url = quality_url; console.log('Player', 'quality selected url', quality_url); } } } var lauch = function lauch() { // Запоминаем текущий объект, чтобы потом можно было получить его в других методах work = data; // Если есть реклама, то показываем её, затем запускаем плеер Preroll.show(data, function () { listener$7.send('start', data); console.log('Player', 'play url', data.url); PlayerVideo.url(Torserver.toPlayUrl(data.url)); toggle$5(); Timeline.needToContinue(toggle$5); listener$7.send('ready', data); }); }; if (launch_player) data.launch_player = launch_player; start$6(data, data.torrent_hash ? 'torrent' : '', lauch); } function iptv(data) { PlayerIPTV.locked(data, function () { console.log('Player', 'play iptv'); data.iptv = true; //пометка для ведра, что это iptv data.iptv_player = true; //помечаем как iptv плеер, для него совсем другой интерфейс var lauch = function lauch() { work = data; html$c.toggleClass('iptv', Boolean(data.iptv)); listener$7.send('start', data); PlayerIPTV.start(data); toggle$5(); listener$7.send('ready', data); }; start$6(data, 'iptv', function () { if (data.vast_url) Preroll.show(data, lauch);else lauch(); }); }); } /** * Установить плейлист * @doc * @name playlist * @alias Player * @param {array} data JSON([{"title":"{string} - Серия 1", "url":"{string} - http://example/video.mp4"}]) */ function playlist(playlist) { PlayerPlaylist.set(playlist); } /** * Установить субтитры для видео * @doc * @name subtitles * @alias Player * @param {array} subs JSON([{"index":"{integer}", "label":"{string}", "url":"http://example/subs.srt"}]) */ function subtitles(subs) { Subtitles.custom(subs); } /** * Запустить другой плеер * @doc * @name runas * @alias Player * @param {string} need android, ios, webos, apple, apple_tv, macos, desktop, other */ function runas(need) { launch_player = need; } /** * Обратный вызов при закрытии плеера * @doc * @name callback * @alias Player * @param {function} back */ function onBack(back) { callback$2 = back; } /** * Рендер плеера * @doc * @name render * @alias Player * @returns Html */ function render$7() { return html$c; } /** * Возвращает статус, открыт ли плеер * @doc * @name opened * @alias Player * @returns {boolean} */ function opened$1() { return is_opened; } /** * Показать процесс загрузки * @doc * @name loading * @alias Player * @param {boolean} status cтатус загрузки, `true` - показать, `false` - скрыть */ function loading$1(status) { if (!work) return; wait_loading = status; html$c.toggleClass('player--loading', Boolean(status)); if (wait_loading) { Controller.toggle('player-loading'); PlayerVideo.pause(); } else { PlayerVideo.play(); toggle$5(); } } /** * Уничтожить плеер */ function destroy$4(keepPip) { Timeline.destroy(); if (work.viewed) work.viewed(Storage.field('torrserver_tracktimecode') ? Math.round(PlayerVideo.video().currentTime) : viewing.time); work = false; wait_for_loading_url = false; wait_loading = false; viewing.time = 0; viewing.difference = 0; viewing.current = 0; html$c.removeClass('player--ios'); html$c.removeClass('iptv'); html$c.removeClass('iptv--player'); html$c.removeClass('player--panel-visible'); html$c.removeClass('player--loading'); PlayerVideo.destroy(false, keepPip); PlayerVideo.clearParamas(); if (!keepPip) html$c.detach(); is_opened = false; Background.theme('reset'); $('body').removeClass('player--viewing'); if (Select.opened()) Select.hide(); listener$7.send('destroy', {}); } var Player = { init: init$G, listener: listener$7, toggle: toggle$5, play: play$1, playlist: playlist, render: render$7, subtitles: subtitles, runas: runas, callback: onBack, opened: opened$1, iptv: iptv, programReady: PlayerIPTV.programReady, close: backward$1, getUrlQuality: getUrlQuality, loading: loading$1, timecodeRecording: Timeline.setRecording, playdata: function playdata() { return work; } }; var html$b = $("\n
    \n \n \n \n
    "); var background = { one: { canvas: $('.background__one', html$b), ctx: $('.background__one', html$b)[0].getContext('2d') }, two: { canvas: $('.background__two', html$b), ctx: $('.background__two', html$b)[0].getContext('2d') }, fade: { canvas: $('.background__fade', html$b), ctx: $('.background__fade', html$b)[0].getContext('2d') } }; var theme_elem = $('head meta[name="theme-color"]'); var view = 'one'; var src = ''; var loaded$1 = {}; var bokeh = { c: [], h: [], d: true }; var timer$2; var immed_time = Date.now(); var theme_color = '#000000'; var timer_change; /** * Запуск */ function init$F() { Storage.listener.follow('change', function (event) { if (event.name == 'background' || event.name == 'background_type') resize$1(); }); var u = Platform.any() ? object$2.github_lampa : './'; if (Platform.is('orsay')) { u = './'; } for (var i = 1; i <= 6; i++) { var im = new Image(); im.src = u + 'img/bokeh-h/' + i + '.png'; bokeh.h.push(im); } for (var _i = 1; _i <= 6; _i++) { var _im = new Image(); _im.src = u + 'img/bokeh/' + _i + '.png'; bokeh.c.push(_im); } background.one.canvas[0].width = window.screen_width; background.one.canvas[0].height = window.screen_height; background.two.canvas[0].width = window.screen_width; background.two.canvas[0].height = window.screen_height; background.fade.canvas[0].width = window.screen_width; background.fade.canvas[0].height = window.screen_height; Lampa.Listener.follow('resize_start', function () { html$b[0].style.opacity = 0; }); Lampa.Listener.follow('resize_end', function () { html$b[0].style.opacity = 1; resize$1(); }); html$b[0].toggleClass('hide', !Storage.field('background')); } /** * Получить активный фон * @returns {{canvas:object, ctx: class}} */ function bg() { view = view == 'one' ? 'two' : 'one'; return background[view]; } /** * Рисовать * @param {object} data * @param {object} item - фон * @param {boolean} noimage */ function draw(data, item, noimage) { if (noimage) return; // Для мобильных устройств делаем полный сброс canvas if (Platform.screen('mobile')) { item.canvas[0].width = window.screen_width; item.canvas[0].height = window.screen_height; } else { item.ctx.clearRect(0, 0, window.screen_width, window.screen_height); } var palette = data.palette; var type = Storage.field('background_type'); blur(data, item, function () { if (type == 'complex' && bokeh.d) { var bright = Color.rgbToHsl(palette.average[0], palette.average[1], palette.average[2]); item.ctx.globalAlpha = bright[2] > 30 ? bright[2] / 100 * 0.6 : 0.4; item.ctx.globalCompositeOperation = bright[2] > 30 ? 'color-dodge' : 'screen'; for (var i = 0; i < 10; i++) { var bp = Math.round(Math.random() * (bokeh.c.length - 1)); var im = bright[2] > 30 ? bokeh.h[bp] : bokeh.c[bp]; var xp = window.screen_width * Math.random(), yp = window.screen_height / 2 * Math.random() + window.screen_height / 2, sz = Math.max(window.screen_height / 8, window.screen_height / 5 * Math.random()) * 0.01, nw = im.width * sz, nh = im.height * sz; try { item.ctx.drawImage(im, xp - nw / 2, yp - nw / 2, nw, nh); } catch (e) {} } } item.ctx.globalAlpha = type == 'poster' ? 0.7 : 0.6; item.ctx.globalCompositeOperation = 'multiply'; var angle = 90 * Math.PI / 180, x2 = window.screen_width * Math.cos(angle), y2 = window.screen_height * Math.sin(angle); var gradient = item.ctx.createLinearGradient(0, 0, x2, y2); gradient.addColorStop(0, 'rgba(0,0,0,1)'); gradient.addColorStop(1, 'rgba(0,0,0,' + (type == 'poster' ? 0.4 : 0) + ')'); item.ctx.fillStyle = gradient; item.ctx.fillRect(0, 0, window.screen_width, window.screen_height); if (Platform.screen('mobile')) { item.ctx.globalAlpha = 1; item.ctx.globalCompositeOperation = 'destination-out'; gradient = item.ctx.createLinearGradient(0, 0, x2, y2); gradient.addColorStop(0.05, 'rgba(29,31,32,1)'); gradient.addColorStop(0.18, 'rgba(' + palette.bright.join(',') + ',0)'); item.ctx.fillStyle = gradient; item.ctx.fillRect(0, 0, window.screen_width, window.screen_height); clearTimeout(timer_change); timer_change = setTimeout(function () { html$b.find('canvas').eq(view == 'one' ? 1 : 0).removeClass('visible'); }, 400); item.canvas.addClass('visible'); } else { fadeTo(item); } if (!Player.opened()) theme(Storage.field('black_style') ? 'black' : 'reset'); }); } /** * Плавный переход с двойным прогревом обоих canvas */ function fadeTo(new_bg) { var fade = background.fade.ctx; var old_image = background[view === 'one' ? 'two' : 'one'].canvas[0]; var new_image = new_bg.canvas[0]; var duration = 700; // длительность fade var warmup = 80; // прогрев GPU var start = Date.now(); function step() { var elapsed = Date.now() - start; if (elapsed < warmup) { // прогрев: оба слоя с альфой 0 fade.globalAlpha = 0; fade.drawImage(old_image, 0, 0); fade.drawImage(new_image, 0, 0); requestAnimationFrame(step); return; } // плавный переход var progress = Math.min((elapsed - warmup) / duration, 1); var eased = progress * progress * (3 - 2 * progress); // easeInOutCubic // сначала рисуем старый фон fade.globalAlpha = 1; fade.drawImage(old_image, 0, 0); // поверх рисуем новый с альфой fade.globalAlpha = eased; fade.drawImage(new_image, 0, 0); if (progress < 1) { requestAnimationFrame(step); } } // небольшой defer для синхронизации с рендером браузера requestAnimationFrame(function () { return requestAnimationFrame(step); }); } /** * Размыть картинку * @param {object} data * @param {object} item - фон * @param {function} complite */ function blur(data, item, complite) { function blured(img) { var ratio = Math.max(window.screen_width / img.width, window.screen_height / img.height); var nw = img.width * ratio, nh = img.height * ratio; item.ctx.globalAlpha = data.img.width > 1000 ? bokeh.d ? 1 : 0.2 : 1; item.ctx.drawImage(img, -(nw - window.screen_width) / 2, -(nh - window.screen_height) / 2, nw, nh); complite(); } if (data.img.width > 1000) blured(data.img);else Color.blur(data.img, blured); } /** * Обновить если изменился размер окна */ function resize$1() { background.one.canvas[0].width = window.screen_width; background.one.canvas[0].height = window.screen_height; background.two.canvas[0].width = window.screen_width; background.two.canvas[0].height = window.screen_height; background.fade.canvas[0].width = window.screen_width; background.fade.canvas[0].height = window.screen_height; background.one.canvas.width(window.screen_width); background.one.canvas.height(window.screen_height); background.two.canvas.width(window.screen_width); background.two.canvas.height(window.screen_height); background.fade.canvas.width(window.screen_width); background.fade.canvas.height(window.screen_height); html$b[0].toggleClass('hide', !Storage.field('background')); if (loaded$1[src]) draw(loaded$1[src], background[view]); } /** * Максимум картинок в памяти */ function limit$1() { var a = Arrays.getKeys(loaded$1); if (a.length > 30) { var u = a.slice(0, 1); delete loaded$1[u]; } } /** * Загрузить картинку в память */ function load$1() { if (loaded$1[src]) { draw(loaded$1[src], bg()); } else if (src) { limit$1(); var cache_src = src; var colors; var img = new Image(); img.crossOrigin = "Anonymous"; img.onload = function () { try { colors = Color.get(img); } catch (e) { colors = [[200, 200, 200], [100, 100, 100], [10, 10, 10]]; } loaded$1[cache_src] = { img: img, palette: Color.palette(colors) }; draw(loaded$1[cache_src], bg()); }; img.onerror = function () { draw(false, false, true); }; img.src = src; } } /** * Изменить картинку * @param {string} url */ function change() { var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; if (url == src || Storage.field('light_version') || !Storage.field('background')) return; bokeh.d = true; if (url) src = url; clearTimeout(timer$2); timer$2 = setTimeout(function () { if (url) load$1();else draw(false, false, true); }, 1000); } /** * Изменить немедленно без ожидания * @param {string} url */ function immediately() { var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; if (Storage.field('light_version') || immed_time + 1000 > Date.now() || !Storage.field('background')) return; if (url) src = url; clearTimeout(timer$2); bokeh.d = false; immed_time = Date.now(); if (url) load$1();else draw(false, false, true); } function theme(color) { if (color == 'black') color = '#000000';else if (color == 'reset') color = '#1d1f20'; if (color == theme_color) return; theme_color = color; theme_elem.attr('content', color); } /** * Рендер * @returns {object} */ function render$6() { return html$b; } var Background = { render: render$6, change: change, update: resize$1, init: init$F, immediately: immediately, theme: theme }; var timer$1; var need_update = false; var need_visible = false; function init$E() { window.screen_width = window.innerWidth; window.screen_height = window.innerHeight; $(window).on('resize', function () { Lampa.Listener.send('resize_start'); window.screen_width = window.innerWidth; window.screen_height = window.innerHeight; clearTimeout(timer$1); timer$1 = setTimeout(function () { toggleMobile(); toggleOrientation(); size(); update$5(); Lampa.Listener.send('resize_end'); }, 100); }); var follow = ['animation', 'mask', 'card_interfice_poster', 'glass_style', 'black_style', 'glass_opacity', 'card_interfice_cover', 'advanced_animation', 'light_version', 'keyboard_type', 'menu_always']; Storage.listener.follow('change', function (event) { if (event.name == 'interface_size') { size(); update$5(); } if (follow.indexOf(event.name) >= 0) toggleClasses(), update$5(); }); toggleClasses(); toggleMobile(); toggleOrientation(); size(); setTimeout(blick, 5000); if (Platform.tv() || Platform.desktop()) mouseEvents(); } function toggleMobile() { $('body').toggleClass('true--mobile', Platform.screen('mobile')); } function toggleOrientation() { $('body').removeClass('orientation--portrait orientation--landscape').addClass('orientation--' + (window.innerWidth > window.innerHeight && window.innerHeight < 768 ? 'landscape' : 'portrait')); } function mouseEvents() { var body = $('body'); var mouse_timer_cursor; $(window).on('mousemove', function () { clearTimeout(mouse_timer_cursor); mouse_timer_cursor = setTimeout(function () { body.toggleClass('no--cursor', true); }, 3000); body.toggleClass('no--cursor', false); }); } function size() { var sl = Storage.field('interface_size'); var sz = { normal: 1, small: 0.9, bigger: 1.05 }; var fs = sz[sl]; $('body').css({ fontSize: Math.max(window.innerWidth / 84.17 * fs, 10.6) + 'px' }).removeClass('size--small size--normal size--bigger').addClass('size--' + sl); } function blick() { $('.icon--blink').not('.ready').each(function () { var elem = $(this), time = parseInt(elem.data('blink-interval') || 3) * 1000; elem.addClass('ready'); setInterval(function () { elem.addClass('animate'); setTimeout(function () { elem.removeClass('animate'); }, 1000); }, time); }); } function frameUpdate(render) { var where = render || document.body; var target = where instanceof jQuery ? where[0] : where; var landscape = window.innerWidth > window.innerHeight && window.innerHeight < 768; var wrap = document.querySelector('.wrap__left'); var head = document.querySelector('.head'); var navi = document.querySelector('.navigation-bar'); var menu_left = wrap ? wrap.getBoundingClientRect().left : 0; var menu_width = wrap ? wrap.getBoundingClientRect().width : 0; var head_height = head ? head.getBoundingClientRect().height : 0; var navi_height = navi && !landscape ? navi.getBoundingClientRect().height : 0; var navi_width = navi && landscape ? navi.getBoundingClientRect().width : 0; var layer_width = Array.from(target.querySelectorAll('.layer--width')); var layer_height = Array.from(target.querySelectorAll('.layer--height')); var layer_wheight = Array.from(target.querySelectorAll('.layer--wheight')); if (target.classList.contains('layer--width')) layer_width.push(target); if (target.classList.contains('layer--height')) layer_height.push(target); if (target.classList.contains('layer--wheight')) layer_wheight.push(target); // Check for 'always' menu mode to determine the layout offset var menu_offset = document.body.classList.contains('menu--always') ? menu_width : 0; for (var i = 0; i < layer_width.length; i++) { var elem = layer_width[i], read = parseFloat(elem.style.width), widh = window.innerWidth - (Platform.screen('light') && menu_left == 0 ? menu_width : menu_offset) - navi_width; if (read !== widh) layer_width[i].style.width = widh; } for (var _i = 0; _i < layer_wheight.length; _i++) { var _elem = layer_wheight[_i], heig = window.innerHeight - head_height - navi_height, attr = _elem.mheight, _read = parseFloat(_elem.style.height); if (attr) { heig -= attr.getBoundingClientRect().height; } if (_read !== heig) _elem.style.height = heig; } for (var _i2 = 0; _i2 < layer_height.length; _i2++) { var _elem2 = layer_height[_i2], _heig = window.innerHeight, _attr = _elem2.mheight, _read2 = parseFloat(_elem2.style.height); if (_attr) { _heig -= _attr.getBoundingClientRect().height; } if (_read2 !== _heig) _elem2.style.height = _heig; } } function intersectedGrid(viewCell, elemCell) { var maxDistance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 2; // разница между экранной ячейкой и ячейкой элемента var dx = Math.abs(viewCell.x - elemCell.x); var dy = Math.abs(viewCell.y - elemCell.y); // если элемент в пределах maxDistance ячеек — показываем return dx <= maxDistance && dy <= maxDistance; } function frameVisible() { var cellW = window.innerWidth; var cellH = window.innerHeight; // экран всегда "в нулевой ячейке" var viewCell = { x: 0, y: 0 }; if (need_visible) { var elems = need_visible; for (var i = 0; i < elems.length; i++) { var elem = elems[i]; if (!elem.call_visible) { elem.bond = elem.getBoundingClientRect(); // определяем в какую ячейку попадает элемент var elemCell = { x: Math.floor(elem.bond.left / cellW), y: Math.floor(elem.bond.top / cellH) }; var inter = intersectedGrid(viewCell, elemCell, 2); elem.visible = inter; var visibility = inter ? 'visible' : 'hidden'; if (elem.visibility !== visibility) { if (!elem.visibility && visibility == 'visible') continue; elem.visibility = visibility; } } } for (var _i3 = 0; _i3 < elems.length; _i3++) { var _elem3 = elems[_i3]; if (_elem3.visible && !_elem3.called_visible) { _elem3.called_visible = true; Utils$1.trigger(_elem3, 'visible'); } if (_elem3.visibility && _elem3.style.visibility !== _elem3.visibility) { _elem3.style.visibility = _elem3.visibility; Utils$1.trigger(_elem3, 'visible:change'); } } } } function toggleClasses() { $('body').toggleClass('no--animation', !Storage.field('animation')); $('body').toggleClass('no--mask', !Storage.field('mask')); $('body').toggleClass('no--poster', !Storage.field('card_interfice_poster')); $('body').toggleClass('glass--style', Storage.field('glass_style')); $('body').toggleClass('black--style', Storage.field('black_style')); $('body').toggleClass('card--no-cover', !Storage.field('card_interfice_cover')); $('body').toggleClass('advanced--animation', Storage.field('advanced_animation')); $('body').toggleClass('light--version', Storage.field('light_version')); $('body').toggleClass('system--keyboard', Storage.field('keyboard_type') == 'lampa' ? false : true); $('body').toggleClass('menu--always', Platform.screen('tv') && Storage.field('menu_always')); $('body').removeClass('glass--style-opacity--easy glass--style-opacity--medium glass--style-opacity--blacked'); if (Storage.field('glass_style')) $('body').addClass('glass--style-opacity--' + Storage.field('glass_opacity')); Background.theme(Storage.field('black_style') ? 'black' : 'reset'); } function combineElements(find_class, elements, combine) { var target = combine instanceof jQuery ? combine[0] : combine; var elems = []; if (target.classList.contains(find_class)) { elems.push(target); } elems = elems.concat(Array.from(target.querySelectorAll('.' + find_class))); elems.filter(function (elem) { return elements.indexOf(elem) < 0; }); return [].concat(elements, elems); } function visible(where) { var active = Lampa.Activity.active(); var combine = where ? where : active && active.activity ? active.activity.render(true) : false; if (!combine) return; need_visible = combineElements('layer--visible', need_visible || [], combine); updateFrame(); } function update$5(where) { need_update = where; updateFrame(); } function updateFrame() { if (need_update !== false) frameUpdate(need_update); if (need_visible !== false) frameVisible(); need_update = false; need_visible = false; } var Layer = { update: update$5, visible: visible, init: init$E }; /** * Создает кастомный скролл * @param {object} [params] - параметры скрола * @param {boolean} [params.horizontal=false] - горизонтальный скролл * @param {boolean} [params.mask=false] - отображать маску прокрутки * @param {boolean} [params.over=false] - разрешить скролл поверх других элементов * @param {boolean} [params.nopadding=false] - отключить паддинги * @param {boolean} [params.notransition=false] - отключить анимацию прокрутки * @param {number} [params.step=150] - шаг прокрутки колесом мыши * @param {boolean} [params.scroll_by_item=false] - прокручивать по элементам * @param {number} [params.end_ratio=1] - соотношение для определения конца скрола (1 - конец, 2 - середина и т.д.) * @param {function} [this.onWheel] - вызывается при прокрутке колесом мыши, вместо стандартной функции * @param {function} [this.onScroll] - вызывается при прокрутке скрола * @param {function} [this.onEnd] - вызывается при достижении конца скрола * @returns {Scroll} */ function Scroll() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _self = this; var html = Template.js('scroll'); var body = html.querySelector('.scroll__body'); var content = html.querySelector('.scroll__content'); var screen = Platform.screen('tv'); var scroll_position = 0; var scroll_animating = false; var time_call_end = Date.now(); var scroll_time = 0, scroll_step = params.step || 150; if (params.horizontal) html.classList.toggle('scroll--horizontal', true); if (params.mask) html.classList.toggle('scroll--mask', true); if (params.over) html.classList.toggle('scroll--over', true); if (params.nopadding) html.classList.toggle('scroll--nopadding', true); if (params.notransition) body.classList.toggle('notransition', true); function wheel(e) { var parent = $(e.target).parents('.scroll'); var inner = onTheRightSide(e, true); if (!params.horizontal && $(html).is(parent[0])) inner = true; if (Date.now() - scroll_time > 200 && inner) { scroll_time = Date.now(); if (e.wheelDelta / 120 > 0) { if (_self.onWheel) _self.onWheel(-scroll_step);else _self.wheel(-scroll_step); } else { if (_self.onWheel) _self.onWheel(scroll_step);else _self.wheel(scroll_step); } } } // Обрабатываем скролл колесом мыши html.addEventListener('mousewheel', wheel); html.addEventListener('wheel', wheel); // Экспортируемые методы и свойства html.Scroll = _self; // Если это телевизор и есть сенсорный экран, то делаем скролл тачем if (screen) { var movestart = function movestart(e) { start_position = params.horizontal ? e.clientX : e.clientY; end_position = start_position; move_position = start_position; body.toggleClass('notransition', true); }; var move = function move(e) { end_position = params.horizontal ? e.clientX : e.clientY; if (move_position && end_position) { var delta = move_position - end_position; var direct = params.horizontal ? 'left' : 'top'; var scrl = scroll_position, scrl_padding = parseInt(window.getComputedStyle(content, null).getPropertyValue('padding-' + direct)); var max = params.horizontal ? 30000 : body.offsetHeight; max -= params.horizontal ? html.offsetWidth : html.offsetHeight; max += scrl_padding * 2; scrl -= delta; scrl = Math.min(0, Math.max(-max, scrl)); scrl = maxOffset(scrl); scroll_position = scrl; translateScroll(); move_position = end_position; } }; var moveend = function moveend(e) { end_position = 0; start_position = 0; move_position = 0; body.toggleClass('notransition', false); scrollEnded(); if (_self.onAnimateEnd) _self.onAnimateEnd(); }; html.addEventListener('scroll', function (e) { html.scrollTop = 0; html.scrollLeft = 0; }); var start_position = 0; var move_position = 0; var end_position = 0; html.addEventListener('touchstart', function (e) { movestart(e.touches[0] || e.changedTouches[0]); }); html.addEventListener('touchmove', function (e) { move(e.touches[0] || e.changedTouches[0]); }); html.addEventListener('touchend', moveend); } else { var native_scroll_animate = false; var native_scroll_timer = null; html.addEventListener('scroll', function () { clearTimeout(native_scroll_timer); native_scroll_timer = setTimeout(function () { if (_self.onAnimateEnd) _self.onAnimateEnd(); }, 300); if (!native_scroll_animate) { native_scroll_animate = true; requestAnimationFrame(function () { native_scroll_animate = false; scroll_position = -(params.horizontal ? html.scrollLeft : html.scrollTop); scrollEnded(); }); } }); } /** * Определяет, находится ли курсор на правой стороне скрола * @param {Event} e - событие мыши * @param {boolean} inleft - если true, то проверяет левую сторону * @returns {boolean} */ function onTheRightSide(e) { var inleft = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var offset = content.getBoundingClientRect().left; var width = window.innerWidth - offset; var position = e.clientX - offset; return params.horizontal ? position > width / 2 : inleft ? position < width / 2 : false; } /** * Максимально возможный отступ * @param {number} offset - желаемый отступ * @returns {number} */ function maxOffset(offset) { var w = params.horizontal ? html.offsetWidth : html.offsetHeight; var p = parseInt(window.getComputedStyle(content, null).getPropertyValue('padding-' + (params.horizontal ? 'left' : 'top'))); var s = body[params.horizontal ? 'scrollWidth' : 'scrollHeight']; offset = Math.min(0, offset); offset = Math.max(-(Math.max(s + p * 2, w) - w), offset); return offset; } /** * Вызов обновления скрола и вызов события конеца скрола */ function scrollEnded() { if (_self.onScroll) _self.onScroll(!screen ? html[params.horizontal ? 'scrollLeft' : 'scrollTop'] : -scroll_position);else Layer.visible(html); if (_self.onEnd && _self.isEnd()) _self.onEnd(); } /** * Перемещает скролл в указанную позицию без анимации */ function translateScroll() { if (!screen) { html[params.horizontal ? 'scrollLeft' : 'scrollTop'] = -scroll_position; } else { body.style['-webkit-transform'] = 'translate3d(' + Math.round(params.horizontal ? scroll_position : 0) + 'px, ' + Math.round(params.horizontal ? 0 : scroll_position) + 'px, 0px)'; } } /** * Начинает анимацию прокрутки к указанной позиции * @param {number} to_position - куда нужно прокрутить */ function startScroll(to_position) { // Зачем начинать анимацию, если мы уже там? if (scroll_position == to_position) { if (!_self.isFilled()) scrollEnded(); return; } scroll_position = to_position; translateScroll(); if (Storage.field('animation')) { scroll_animating = true; body.addEventListener('webkitTransitionEnd', function () { if (Date.now() - time_call_end < 300) return; time_call_end = Date.now(); scroll_animating = false; scrollEnded(); if (_self.onAnimateEnd) _self.onAnimateEnd(); }, { once: true }); } else { scrollEnded(); if (_self.onAnimateEnd) _self.onAnimateEnd(); } } /** * Получить позицию элемента относительно скрола * @param {HTMLElement|jQuery} elem - элемент, который должен быть виден * @param {boolean} tocenter - выровнять элемент по центру скрола * @returns {number} */ function getElementPosition(elem, tocenter) { var dir = params.horizontal ? 'left' : 'top', siz = params.horizontal ? 'offsetWidth' : 'offsetHeight'; var target = elem instanceof jQuery ? elem[0] : elem; var p = tocenter ? parseInt(window.getComputedStyle(content, null).getPropertyValue('padding-' + (params.horizontal ? 'left' : 'top'))) : 0; var ofs_elm = target.getBoundingClientRect()[dir], ofs_box = body.getBoundingClientRect()[dir], center = ofs_box + (tocenter ? content[siz] / 2 - target[siz] / 2 - p : 0), scrl = Math.min(0, center - ofs_elm); scrl = maxOffset(scrl); return scrl; } /** * Добавить обработку свайпа вниз имитации скрытия шторки * @param {function} call - функция, которая будет вызвана при свайпе вниз */ this.addSwipeDown = function (call) { if (window.innerWidth > 480) return; var s = 0; var t = 0; html.addEventListener('touchstart', function (e) { var point = e.touches[0] || e.changedTouches[0]; if (s == 0) { s = point.clientY; t = Date.now(); } }); html.addEventListener('touchmove', function (e) { var point = e.touches[0] || e.changedTouches[0]; if (s !== 0) { if (point.clientY - s > 50 && html.scrollTop == 0 && Date.now() - t < 100) { s = 0; call(); } } }); html.addEventListener('touchend', function (e) { s = 0; }); }; /** * Вызвать прокрутку * @param {number} size - на сколько пикселей прокрутить */ this.wheel = function (size) { var direct = params.horizontal ? 'left' : 'top'; var scrl = scroll_position, scrl_offset = html.getBoundingClientRect()[direct], scrl_padding = parseInt(window.getComputedStyle(content, null).getPropertyValue('padding-' + direct)); if (params.scroll_by_item) { var pos = body.scroll_position; pos = pos || 0; var items = Array.from(body.children); pos += size > 0 ? 1 : -1; pos = Math.max(0, Math.min(items.length - 1, pos)); body.scroll_position = pos; var item = items[pos], ofst = item.getBoundingClientRect()[direct]; size = ofst - scrl_offset - scrl_padding; } var max = params.horizontal ? 30000 : body.offsetHeight; max -= params.horizontal ? html.offsetWidth : html.offsetHeight; max += scrl_padding * 2; scrl -= size; scrl = Math.min(0, Math.max(-max, scrl)); scrl = maxOffset(scrl); startScroll(scrl); }; /** * Обновить позицию скрола, чтобы элемент был виден * @param {HTMLElement|jQuery} elem - элемент, который должен быть виден * @param {boolean} tocenter - выровнять элемент по центру скрола * @returns {void} */ this.update = function (elem, tocenter) { startScroll(getElementPosition(elem, tocenter)); }; /** * Обновить позицию скрола, чтобы элемент был виден без анимации * @param {HTMLElement|jQuery} elem - элемент, который должен быть виден * @param {boolean} tocenter - выровнять элемент по центру скрола * @returns {void} */ this.immediate = function (elem, tocenter) { body.toggleClass('notransition', true); scroll_position = getElementPosition(elem, tocenter); translateScroll(); setTimeout(function () { body.toggleClass('notransition', false); }, 5); }; /** * Сдвинуть скролл на указанное количество пикселей без анимации * @param {number} shift - на сколько пикселей сдвинуть скролл * @returns {void} */ this.shift = function (shift) { body.toggleClass('notransition', true); scroll_position = maxOffset(scroll_position - shift); translateScroll(); setTimeout(function () { body.toggleClass('notransition', false); }, 5); }; /** * Получить параметры скрола * @returns {Object} */ this.vieport = function () { var vieport = {}; if (!screen) { vieport.position = html[params.horizontal ? 'scrollLeft' : 'scrollTop'], vieport.body = body[params.horizontal ? 'scrollWidth' : 'scrollHeight'], vieport.content = html[params.horizontal ? 'offsetWidth' : 'offsetHeight']; } else { vieport.position = scroll_position; vieport.body = body[params.horizontal ? 'offsetWidth' : 'offsetHeight']; vieport.content = html[params.horizontal ? 'offsetWidth' : 'offsetHeight']; } return vieport; }; /** * Проверить, достигнут ли конец скрола * @param {number} [end_ratio] - соотношение для определения конца скрола (1 - конец, 2 - середина и т.д.) * @returns {boolean} */ this.isEnd = function (end_ratio) { var vieport = this.vieport(); if (vieport.body < vieport.content) return vieport.position <= 0; return vieport.body - vieport.content * Math.max(1, end_ratio || params.end_ratio || 1) < Math.abs(vieport.position); }; /** * Проверить, заполнен ли скролл контентом * @returns {boolean} */ this.isFilled = function () { var vieport = this.vieport(); return vieport.body > vieport.content; }; /** * Добавить элемент в скролл * @param {HTMLElement|jQuery} object - элемент для добавления * @returns {void} */ this.append = function (object) { body.append(object); }; /** * Добавить элемент в начало скрола * @param {HTMLElement|jQuery} object - элемент для добавления * @returns {void} */ this.prepend = function (object) { body.prepend(object); }; /** * Установить высоту скрола с учетом вычитаемого элемента и вычитом шапки * @param {HTMLElement|jQuery} minus - элемент, высота которого будет вычтена из высоты скрола * @returns {void} */ this.minus = function (minus) { html.classList.add('layer--wheight'); html.mheight = minus instanceof jQuery ? minus[0] : minus; }; /** * Установить высоту скрола с учетом вычитаемого элемента * @param {HTMLElement|jQuery} minus - элемент, высота которого будет вычтена из высоты скрола * @returns {void} */ this.height = function (minus) { html.classList.add('layer--height'); html.mheight = minus instanceof jQuery ? minus[0] : minus; }; /** * Получить тело скрола * @param {boolean} js - вернуть в виде DOM-элемента, а не jQuery * @returns {HTMLElement|jQuery} */ this.body = function (js) { return js ? body : $(body); }; /** * Получить HTML скрола * @param {boolean} js - вернуть в виде DOM-элемента, а не jQuery * @returns {HTMLElement|jQuery} */ this.render = function (js) { return js ? html : $(html); }; /** * Очистить скролл от элементов * @returns {void} */ this.clear = function () { body.innerHTML = ''; }; /** * Получить параметры скрола * @returns {Object} */ this.params = function () { return params; }; /** * Получить текущую позицию скрола * @returns {number} */ this.position = function () { return scroll_position; }; /** * Восстановить позицию скрола * Используется после detach активности, скролл сбивается и после start активности его нужно восстановить * @returns {void} */ this.restorePosition = function () { if (Platform.screen('mobile')) translateScroll(); }; /** * Отключить паддинги у скрола * @returns {void} */ this.nopadding = function () { html.classList.toggle('scroll--nopadding', true); }; /** * Cбросить позицию скрола в 0 * @returns {void} */ this.reset = function () { scroll_position = 0; translateScroll(); }; /** * Проверить, анимируется ли скролл в данный момент * @returns {boolean} */ this.animated = function () { return scroll_animating ? true : false; }; /** * Удалить скролл * @returns {void} */ this.destroy = function () { scroll_animating = false; html.remove(); }; } var html$a, active$4, _scroll, last$1, _opened = false; var listener$6 = Subscribe$2(); /** * Открывает модальное окно * @param {object} params - параметры окна * @param {string} params.title - заголовок окна * @param {jQuery|HTMLElement} params.html - содержимое окна * @param {string} [params.size=small] - размер окна (small, medium, large, full) * @param {boolean} [params.overlay=false] - отображать окно как оверлей * @param {string} [params.align=top] - выравнивание окна (top, center) * @param {boolean} [params.mask=false] - отображать маску прокрутки * @param {Array} [params.buttons] - массив кнопок внизу окна {name: 'Имя', onSelect: function(){}} * @param {string} [params.buttons_position=inside] - положение кнопок (inside, outside) * @param {HTMLElement|jQuery} [params.select] - элемент для фокуса после открытия * @param {function} [params.onBack] - вызывается при закрытии окна * @param {function} [params.onSelect] - вызывается при выборе элемента внутри окна * @param {number} [params.zIndex] - z-index окна * @returns {void} */ function open$5(params) { active$4 = params; active$4.open_time = Date.now(); listener$6.send('preshow', { active: active$4 }); html$a = Template.get('modal', { title: params.title }); html$a.on('mousedown', function (e) { if (!$(e.target).closest($('.modal__content', html$a)).length && DeviceInput.canClick(e.originalEvent) && active$4.open_time + 1000 < Date.now()) Controller.back(); }); title(params.title); html$a.toggleClass('modal--buttons-column', params.size == 'small' ? true : false); html$a.toggleClass('modal--medium', params.size == 'medium' ? true : false); html$a.toggleClass('modal--large', params.size == 'large' ? true : false); html$a.toggleClass('modal--full', params.size == 'full' ? true : false); html$a.toggleClass('modal--overlay', params.overlay ? true : false); html$a.toggleClass('modal--align-center', params.align == 'center' ? true : false); if (params.zIndex) html$a.css('z-index', params.zIndex); _scroll = new Scroll(_objectSpread2({ over: true, mask: params.mask }, params.scroll)); html$a.find('.modal__content').toggleClass('layer--height', params.size == 'full' ? true : false); html$a.find('.modal__body').append(_scroll.render()); bind$1(params.html); _scroll.onWheel = function (step) { roll(step > 0 ? 'down' : 'up'); }; if (params.size == 'full' && Platform.mouse()) { html$a.find('.modal__content').prepend(HeadBackward(params.title || '')); } _scroll.append(params.html); _scroll.addSwipeDown(function () { html$a.addClass('animate-down'); setTimeout(function () { Controller.back(); }, 200); }); if (params.buttons) buttons$1(); $('body').append(html$a); max(); listener$6.send('fullshow', { active: active$4, html: html$a }); toggle$4(params.select); html$a.addClass('animate'); _opened = true; } function max() { var height = window.innerHeight; if (window.innerWidth <= 480) { if (active$4.size == 'full') { height = window.innerHeight - html$a.find('.head-backward').outerHeight(); _scroll.render().css('height', height + 'px'); } else { height = window.innerHeight * 0.6; } } else { height -= _scroll.render().offset().top; if (active$4.size !== 'full') height -= window.innerHeight * 0.1; if (active$4.buttons && active$4.buttons_position == 'outside') height -= html$a.find('.modal__footer').outerHeight() || 0; } _scroll.render().find('.scroll__content').css('max-height', Math.round(height) + 'px'); } function buttons$1() { var footer = $(''); active$4.buttons.forEach(function (button) { var btn = $(''); btn.text(button.name); btn.on('click hover:enter', function (e) { if (DeviceInput.noDubleClick(e)) button.onSelect(); }); footer.append(btn); }); if (active$4.buttons_position == 'outside') { html$a.find('.modal__content').append(footer); } else _scroll.append(footer); } function bind$1(where) { where.find('.selector').on('hover:focus', function (e) { last$1 = e.target; _scroll.update($(e.target), active$4.scroll_to_center); }).on('hover:enter', function (e) { last$1 = e.target; if (active$4.onSelect) active$4.onSelect($(e.target)); }); } function jump(tofoward) { var select = _scroll.render().find('.selector.focus'); if (tofoward) select = select.nextAll().filter('.selector');else select = select.prevAll().filter('.selector'); select = select.slice(0, 10); select = select.last(); if (select.length) { Controller.collectionFocus(select[0], _scroll.render()); } } function roll(direction) { var select = _scroll.render().find('.selector'); if (select.length) { Navigator.move(direction); } else { var step = Math.round(window.innerHeight * 0.15); _scroll.wheel(direction == 'down' ? step : -step); } } function toggle$4(need_select) { Controller.add('modal', { invisible: true, toggle: function toggle() { Controller.collectionSet(_scroll.render()); Controller.collectionFocus(need_select || last$1, _scroll.render()); Layer.visible(_scroll.render(true)); listener$6.send('toggle', { active: active$4, html: html$a }); }, up: function up() { if (active$4.buttons && active$4.buttons_position == 'outside' && (_scroll.isEnd() || !_scroll.isFilled())) { Controller.toggle('modal'); roll('up'); } else roll('up'); }, down: function down() { if (active$4.buttons && active$4.buttons_position == 'outside' && (_scroll.isEnd() || !_scroll.isFilled())) { Controller.collectionSet(html$a.find('.modal__footer')); Controller.collectionFocus(false, html$a.find('.modal__footer')); } else roll('down'); }, right: function right() { if (Navigator.canmove('right')) Navigator.move('right');else jump(true); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else jump(false); }, back: function back() { if (active$4.onBack) active$4.onBack(); } }); Controller.toggle('modal'); } function update$4(new_html) { last$1 = false; _scroll.clear(); _scroll.append(new_html); bind$1(new_html); max(); listener$6.send('update', { active: active$4, html: html$a, new_html: new_html }); toggle$4(active$4.select); } function title(tit) { html$a.find('.modal__title').text(tit); html$a.toggleClass('modal--empty-title', !tit ? true : false); } function destroy$3() { last$1 = false; _scroll.destroy(); html$a.remove(); listener$6.send('close', { active: active$4 }); _opened = false; } function close$6() { destroy$3(); } function render$5() { return html$a; } var Modal = { listener: listener$6, open: open$5, close: close$6, update: update$4, title: title, toggle: toggle$4, render: render$5, scroll: function scroll() { return _scroll; }, opened: function opened() { return _opened; } }; function init$D() { var agent = navigator.userAgent.toLowerCase(); if (typeof webOS !== 'undefined' && webOS.platform.tv === true) { Storage.set('platform', 'webos'); webOS.deviceInfo(function (e) { webOS.sdk_version = parseFloat(e.sdkVersion); }); } else if (typeof webapis !== 'undefined' && typeof tizen !== 'undefined') { Storage.set('platform', 'tizen'); tizen.tvinputdevice.registerKey("MediaPlayPause"); tizen.tvinputdevice.registerKey("MediaPlay"); tizen.tvinputdevice.registerKey("MediaStop"); tizen.tvinputdevice.registerKey("MediaPause"); tizen.tvinputdevice.registerKey("MediaRewind"); tizen.tvinputdevice.registerKey("MediaFastForward"); tizen.tvinputdevice.registerKey("ChannelUp"); tizen.tvinputdevice.registerKey("ChannelDown"); } else if (agent.indexOf("lampa_client") > -1) { Storage.set('platform', 'android'); } else if (agent.indexOf("whaletv") > -1 || agent.indexOf("philips") > -1 || agent.indexOf("nettv") > -1) { Storage.set('platform', 'philips'); } else if (agent.indexOf("ipad") > -1 && window.innerWidth == 1920 && window.innerHeight == 1080) { Storage.set('platform', 'apple_tv'); } else if (agent.indexOf("iphone") > -1 || agent.indexOf("mac os") > -1 && Utils$1.isTouchDevice() || agent.indexOf("macintosh") > -1 && Utils$1.isTouchDevice()) { Storage.set('platform', 'apple'); } else if (typeof nw !== 'undefined') { Storage.set('platform', 'nw'); } else if (agent.indexOf("electron") > -1) { Storage.set('platform', 'electron'); } else if (agent.indexOf("netcast") > -1) { Storage.set('platform', 'netcast'); } else if (agent.indexOf("version/5.1.7 safari/534.57.2") > -1) { Storage.set('platform', 'orsay'); // Версия для тестов на Safari } else if ((agent.indexOf("windows nt") > -1 || agent.indexOf("macintosh") > -1) && !Utils$1.isTouchDevice()) { Storage.set('platform', 'browser'); } else if (agent.indexOf("maple") > -1) { Storage.set('platform', 'orsay'); Orsay.init(); } else { Storage.set('platform', ''); } Storage.set('native', Storage.get('platform') ? true : false); $('body').addClass('platform--' + (get$8() || 'noname')); $('body').toggleClass('touch-device', screen('mobile')); $('body').toggleClass('mouse--controll', mouse()); } /** * Какая платформа * @returns String */ function get$8() { return Storage.get('platform', ''); } /** * Если это платформа * @param {String|Array} need - какая нужна? tizen, webos, android, netcast, orsay, apple, apple_tv, nw, electron * @returns Boolean */ function is(need) { if (Arrays.isArray(need)) return need.indexOf(get$8()) >= 0; return get$8() == need; } /** * Платформы у которых есть возможность изменить плеер * @returns Boolean */ function any() { return is('tizen') || is('webos') || is('android') || is('netcast') || is('orsay') || is('apple') || is('apple_tv') || macOS() || desktop(); } /** * Если это телевизор * @returns Boolean */ function tv() { return is('tizen') || is('webos') || is('orsay') || is('netcast') || is('apple_tv') || tvbox() || Boolean(navigator.userAgent.toLowerCase().match(/tizen|webos/i)); } /** * Если это телевизор на базе приставки (Android TV, Mi Box, Nvidia Shield и т.п.) * @returns Boolean */ function tvbox() { return Boolean(navigator.userAgent.toLowerCase().match(/googletv|google tv|mibox|mitv|smarttv|smart tv|google tv|android tv/i)) || Boolean(navigator.userAgent.toLowerCase().match(/android/i) && !Utils$1.isTouchDevice()); } /** * Если это NW.js или Electron * @returns Boolean */ function desktop() { return is('nw') || is('electron'); } /** * Если навигация мышь или тачпад * @returns Boolean */ function mouse() { return screen('tv') && !tv() || Storage.field('navigation_type') !== 'controll'; } /** * Если это macOS без тача * @returns Boolean */ function macOS() { var agent = navigator.userAgent.toLowerCase(); return agent.indexOf("mac os x") > -1 && !Utils$1.isTouchDevice(); } /** * Версия приложения * @param {String} name - какая нужна? app, android, orsay * @returns String */ function version(name) { if (name == 'app') { return object$2.app_version; } else if (name == 'android') { return AndroidJS.appVersion(); } else if (name == 'orsay') { return curWidget.version; } else { return ''; } } /** * Экран телевизор или мобильный * @param {String} need - какой нужен? tv, mobile, light * @returns Boolean */ function screen(need) { if (need == 'light') { return Storage.field('light_version') && screen('tv'); } var is_tv = true; if (!(tv() || desktop())) { if (Storage.get('is_true_mobile', 'false')) is_tv = false;else if (Boolean(Storage.get('platform', '') == 'apple')) is_tv = false;else if (Boolean(navigator.userAgent.toLowerCase().match(/iphone|ipad/i))) is_tv = false;else if (Utils$1.isTouchDevice()) { if (!Boolean(navigator.userAgent.toLowerCase().match(/(large screen)|googletv|mibox|mitv|smarttv|google tv|android tv/i))) { var ratio = window.devicePixelRatio || 1; var width = Math.ceil(window.screen_width * ratio); var height = Math.ceil(window.screen_height * ratio); is_tv = width > height && width >= 1280; } } } if (need == 'tv') return is_tv; if (need == 'mobile') return !is_tv; return false; } /** * Установка приложения * @param {String} what - что устанавливать? apk * @returns void */ function install$2(what) { if ($('.modal').length) Modal.close(); if (what == 'apk') { var html = Template.js('account_none'); if (tv()) { var code = html.find('.account-modal-split__qr-code'); html.addClass('layer--' + (mouse() ? 'wheight' : 'height')); Utils$1.qrcode(object$2.apk_link_download, code, function () { html.find('.account-modal-split__qr').addClass('hide'); }); } else html.addClass('account-modal-split--mobile'); html.find('.account-modal-split__title').text(Lang.translate('install_app_apk_title')); html.find('.account-modal-split__text').html(Lang.translate('install_app_apk_text')); html.find('.account-modal-split__qr-text').text(Lang.translate('install_app_apk_qr')); html.find('.simple-button').text(Lang.translate('copy_link_buffer')).on('hover:enter', function () { Utils$1.copyTextToClipboard(object$2.apk_link_download, function () { Bell.push({ text: Lang.translate('copy_secuses') }); }); }); Modal.open({ title: '', html: $(html), size: tv() ? 'full' : 'medium', scroll: { nopadding: true }, onBack: function onBack() { Modal.close(); Controller.toggle('content'); } }); } else { Controller.toggle('content'); } } /** * Версия Chrome для Android TV * @returns Number */ function chromeVersion() { var raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); return raw ? parseInt(raw[2], 10) : 0; } /** * Версия виджета для TV * @returns Number */ function widgetVersion() { return window.widget_version || 0; } var Platform = { init: init$D, get: get$8, any: any, is: is, tv: tv, mouse: mouse, desktop: desktop, version: version, screen: screen, install: install$2, macOS: macOS, chromeVersion: chromeVersion, widgetVersion: widgetVersion }; var reqCallback = {}; var timeCallback = {}; function init$C() { if (Platform.is('android')) { Params.listener.follow('button', function (e) { if (e.name === 'reset_player') { resetDefaultPlayer(); } }); Lampa.Listener.follow('state:changed', function (e) { if (e.target == 'favorite' && e.reason == 'update' && (e.method == 'add' || e.method == 'added' || e.method == 'remove')) { updateChannel(e.type); } }); } } function exit() { if (checkVersion(1)) AndroidJS.exit();else $('')[0].click(); } function playHash(SERVER) { var magnet = "magnet:?xt=urn:btih:" + SERVER.hash; if (checkVersion(10)) { var intentExtra = ""; if (SERVER.movie) { intentExtra = { title: "[LAMPA] " + (SERVER.movie.title || 'No title').replace(/\s+/g, ' ').trim(), poster: SERVER.movie.img, media: SERVER.movie.name ? 'tv' : 'movie', data: { lampa: true, movie: SERVER.movie } }; } AndroidJS.openTorrentLink(magnet, JSON.stringify(intentExtra)); } else { $('')[0].click(); } } function openTorrent(SERVER) { if (checkVersion(10)) { var intentExtra = { title: "[LAMPA] " + (SERVER.movie.title || 'No title').replace(/\s+/g, ' ').trim(), poster: SERVER.object.poster, media: SERVER.movie.name ? 'tv' : 'movie', data: { lampa: true, movie: SERVER.movie } }; AndroidJS.openTorrentLink(SERVER.object.MagnetUri || SERVER.object.Link, JSON.stringify(intentExtra)); } else { $('')[0].click(); } } function openPlayer(link, data) { if (data) data.card = resolveCard(data); var updateTimeline = function updateTimeline(elem) { if (elem.timeline) { var new_timeline = Lampa.Timeline.view(elem.timeline.hash); elem.timeline.time = Math.round(new_timeline.time); elem.timeline.duration = Math.round(new_timeline.duration); elem.timeline.percent = new_timeline.percent; timeCallback[elem.timeline.hash] = elem; } }; if (checkVersion(98, true)) { if (data.timeline) { updateTimeline(data); } if (data.playlist) { data.playlist.forEach(function (elem) { updateTimeline(elem); }); } } if (checkVersion(10)) AndroidJS.openPlayer(link, JSON.stringify(data));else $('')[0].click(); } function openYoutube(link) { if (checkVersion(15)) AndroidJS.openYoutube(link);else $('')[0].click(); } function openBrowser(link) { if (checkVersion(484)) AndroidJS.openBrowser(link); } function resetDefaultPlayer() { if (checkVersion(15)) AndroidJS.clearDefaultPlayer(); } function httpReq(data, call) { var index = Math.floor(Math.random() * 5000); reqCallback[index] = { data: data, call: call }; if (checkVersion(16)) AndroidJS.httpReq(JSON.stringify(data), index);else call.error({ responseText: "No Native request" }); } function httpCall(index, callback) { var req = reqCallback[index]; if (req && req.call[callback]) { var resp = AndroidJS.getResp(index); if (!req.data.dataType || req.data.dataType && req.data.dataType.toLowerCase() == 'json' || callback === 'error') { try { resp = JSON.parse(resp); } catch (e) {} } delete reqCallback[index]; req.call[callback](resp); } } function timeCall(timeline) { var hash = timeline.hash; if (timeCallback[hash]) { timeCallback[hash].timeline.handler(timeline.percent, timeline.time, timeline.duration); timeCallback[hash].timeline.percent = timeline.percent; timeCallback[hash].timeline.duration = timeline.duration; timeCallback[hash].timeline.time = timeline.time; delete timeCallback[hash]; } } function voiceStart() { if (checkVersion(25)) AndroidJS.voiceStart();else Lampa.Noty.show("Работает только на Android TV"); } function updateChannel(where) { if (checkVersion(28)) AndroidJS.updateChannel(where); } function checkVersion(needVersion) { var silent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (typeof AndroidJS !== 'undefined') { try { var current = AndroidJS.appVersion().split('-'); var versionCode = current.pop(); if (parseInt(versionCode, 10) >= needVersion) { return true; } else { if (!silent) Bell.push({ text: 'Обновите приложение до версии ' + needVersion }); return false; } } catch (e) { Bell.push({ text: 'Обновите приложение до версии ' + needVersion }); return false; } } else return false; } function resolveCard(data) { if (data && (data.card || data.movie)) return data.card || data.movie; var activity = Activity.active(); if (activity && (activity.card || activity.movie)) return activity.card || activity.movie; return null; } var Android = { init: init$C, exit: exit, openTorrent: openTorrent, openPlayer: openPlayer, playHash: playHash, openYoutube: openYoutube, resetDefaultPlayer: resetDefaultPlayer, httpReq: httpReq, voiceStart: voiceStart, httpCall: httpCall, timeCall: timeCall, updateChannel: updateChannel, openBrowser: openBrowser }; var _connected = true; var protocols = ['https://', 'http://']; function init$B() { Timer.add(1000 * 60 * 15, function () { task$2(); }); } /** * Редирект на живое зеркало * @param {string} to ссылка на зеркало */ function redirect(to) { var domain = to.replace(/http:\/\/|https:\/\//, ''); Storage.set('cub_domain', domain, true); Storage.set('cub_alive', to, true); console.log('Mirrors', 'redirect to', to); } /** * Проверить живые зеркала по протоколу и вернуть массив живых зеркал * @param {string} protocol протокол для проверки * @param {function} callback функция обратного вызова с массивом живых зеркал */ function find(protocol, callback) { var status = new Status(object$2.cub_mirrors.length); status.onComplite = function (data) { var keys = Object.keys(data); if (keys.length == 0) return callback([]); var answered = keys.filter(function (key) { return data[key] == true; }); if (answered.length == 0) { console.error('Mirrors', protocol + ' all offline'); return callback([]); } console.log('Mirrors', protocol + ' online', answered); callback(answered); }; object$2.cub_mirrors.forEach(function (mirror) { check$1(protocol + mirror, function (answered) { if (answered) { console.log('Mirrors', protocol + mirror, 'is online'); status.append(mirror, answered); } else { console.warn('Mirrors', protocol + mirror, 'is offline'); status.error(); } }); }); } /** * Проверить живое зеркало по ссылке и вернуть результат * @param {string} url ссылка для проверки * @param {function} call функция обратного вызова с результатом проверки */ function check$1(url, call) { var random = Math.random() + ''; $.ajax({ url: url + '/api/checker', type: 'POST', data: { data: random }, dataType: 'text', timeout: 1000 * 7, success: function success(str) { if (str == random) call(true);else call(false); }, error: function error() { call(false); } }); } /** * Загрузочная задача для проверки живых зеркал и редиректа на первое живое * @param {function} call функция обратного вызова */ function task$2(call) { if (!window.lampa_settings.mirrors) return call && call(); var status = new Status(protocols.length); _connected = true; status.onComplite = function (data) { var https = data['https://']; var http = data['http://']; var all = [].concat(https.map(function (a) { return 'https://' + a; })).concat(http.map(function (a) { return 'http://' + a; })); console.log('Mirrors', 'answered:', all); if (!all.length) _connected = false;else redirect(all[0]); if (!_connected) Markers.error('mirrors');else Markers.normal('mirrors'); if (call) call(); }; check$1(object$2.cub_alive, function (answered) { console.log('Mirrors', 'first check:', object$2.cub_alive, 'status:', answered); if (answered) { if (call) call(); } else { protocols.forEach(function (protocol) { find(protocol, function (mirrors) { status.append(protocol, mirrors); }); }); } }); } var Mirrors = { init: init$B, task: task$2, connected: function connected() { return _connected; } }; var bad_mirrors = {}; /** * Универсальный запрос * @example * let network = new Request() * network.get('https://site.com/api/method',function(data){console.log(data)},function(error){console.log(error)}) * network.silent('https://site.com/api/method',function(data){console.log(data)},function(error){console.log(error)}) * network.quiet('https://site.com/api/method',function(data){console.log(data)},function(error){console.log(error)}) * network.last('https://site.com/api/method',function(data){console.log(data)},function(error){console.log(error)}) * network.native('https://site.com/api/method',function(data){console.log(data)},function(error){console.log(error)}) * network.timeout(10000) // установить таймаут для всех запросов * network.clear() // очистить все запросы * network.again() // повторить последний запрос * let last = network.latest() // вернуть обьект последненго запроса * let error_text = network.errorDecode(jqXHR, exception) // декодировать ошибку в запросе * let error_code = network.errorCode(jqXHR) // вернуть код ошибки * let error_json = network.errorJSON(jqXHR) // вернуть json ошибки * @returns {Request} */ function Request() { var listener = Subscribe$2(); var _calls = []; var _last; var last_reguest; var need = { timeout: 1000 * 30 }; this.timeout = function (time) { need.timeout = time; }; /** * Видимый запрос * @param {String} url адрес * @param {Function} complite успешно * @param {Function} error ошибка * @param {Object} post_data данные для пост запроса */ this.get = function (url, _complite, _error, post_data) { clear(); go({ url: url, post_data: post_data, start: function start() { listener.send('start'); }, before_complite: function before_complite() { listener.send('before_complite'); }, complite: function complite(data) { if (_complite) _complite(data); }, after_complite: function after_complite() { listener.send('after_complite'); }, before_error: function before_error() { listener.send('before_error'); }, error: function error(data) { if (_error) _error(data); }, after_error: function after_error() { listener.send('after_error'); }, end: function end() { listener.send('end'); } }); }; /** * Тихий запрос, отработает в любом случае * @param {String} url адрес * @param {Function} complite успешно * @param {Function} error ошибка * @param {Object} post_data данные для пост запроса * @param {Object} params дополнительные параметры */ this.quiet = function (url, _complite2, _error2, post_data, params) { var add_params = {}; if (params) { add_params = params; } var data = { url: url, post_data: post_data, complite: function complite(data) { if (_complite2) _complite2(data); }, error: function error(data) { if (_error2) _error2(data); } }; Arrays.extend(data, add_params, true); go(data); }; /** * Бесшумный запрос, сработает прерывание при новом запросе * @param {String} url адрес * @param {Function} complite успешно * @param {Function} error ошибка * @param {Object} post_data данные для пост запроса * @param {Object} params дополнительные параметры */ this.silent = function (url, complite, error, post_data, params) { var add_params = {}; if (params) { add_params = params; } var reguest = { url: url, complite: complite, error: error }; _calls.push(reguest); var data = { url: url, post_data: post_data, complite: function complite(data) { if (_calls.indexOf(reguest) !== -1 && reguest.complite) reguest.complite(data); }, error: function error(data) { if (_calls.indexOf(reguest) !== -1 && reguest.error) reguest.error(data); }, end: function end() { listener.send('end'); } }; Arrays.extend(data, add_params, true); go(data); }; /** * Отработать только последний запрос в стеке * @param {String} url адрес * @param {Function} complite успешно * @param {Function} error ошибка * @param {Object} post_data данные для пост запроса */ this.last = function (url, complite, error, post_data) { var reguest = { url: url, complite: complite, error: error }; _last = reguest; go({ url: url, post_data: post_data, complite: function complite(data) { if (_last && _last.complite) _last.complite(data); }, error: function error(data) { if (_last && _last.error) _last.error(data); }, end: function end() { dispatchEvent({ type: 'load:end' }); } }); }; this["native"] = function (url, complite, error, post_data, params) { var add_params = {}; if (params) { add_params = params; } var reguest = { url: url, complite: complite, error: error }; _calls.push(reguest); var data = { url: url, post_data: post_data, complite: function complite(data) { if (_calls.indexOf(reguest) !== -1 && reguest.complite) reguest.complite(data); }, error: function error(data) { if (_calls.indexOf(reguest) !== -1 && reguest.error) reguest.error(data); }, end: function end() { listener.send('end'); } }; Arrays.extend(data, add_params, true); _native(data); }; /** * Очистить все запросы */ this.clear = function () { _calls = []; }; /** * Повторить запрос * @param {Object} custom */ this.again = function (custom) { if (custom || last_reguest) { go(custom || last_reguest); } }; /** * Вернуть обьект последненго запроса * @returns Object */ this.latest = function () { return last_reguest; }; /** * Декодировать ошибку в запросе * @param {Object} jqXHR * @param {String} exception * @returns String */ this.errorDecode = function (jqXHR, exception) { return errorDecode(jqXHR, exception); }; this.errorCode = function (jqXHR) { return errorCode(jqXHR); }; this.errorJSON = function (jqXHR) { return errorJSON(jqXHR); }; function errorDecode(jqXHR, exception) { if (!Arrays.isObject(jqXHR)) return Lang.translate('network_error'); var msg = ''; var txt = jqXHR.responseText || jqXHR.message || jqXHR.status || ''; if (jqXHR.status === 0 && exception !== 'timeout') { msg = Lang.translate('network_noconnect'); } else if (jqXHR.responseJSON && jqXHR.responseJSON.code) { msg = Lang.translate('network_500').replace('500', jqXHR.responseJSON.code) + (jqXHR.responseJSON.text ? ' [' + jqXHR.responseJSON.text + ']' : ''); } else if (jqXHR.status == 404) { msg = Lang.translate('network_404'); } else if (jqXHR.status == 401) { msg = Lang.translate('network_401'); } else if (jqXHR.status == 500) { msg = Lang.translate('network_500'); } else if (exception === 'parsererror') { msg = Lang.translate('network_parsererror'); } else if (exception === 'timeout') { msg = Lang.translate('network_timeout'); } else if (exception === 'abort') { msg = Lang.translate('network_abort'); } else if (exception === 'custom') { msg = jqXHR.responseText; } else { msg = Lang.translate('network_error') + (txt ? ': ' + txt : ''); } return msg; } function errorCode(jqXHR) { return jqXHR && jqXHR.responseJSON ? jqXHR.responseJSON.code : jqXHR ? jqXHR.status : 404; } function errorJSON(jqXHR) { return jqXHR && jqXHR.responseJSON ? jqXHR.responseJSON : { code: jqXHR ? jqXHR.status : 404, text: jqXHR ? jqXHR.responseText || jqXHR.message || Lang.translate('network_404') : Lang.translate('network_404') }; } function go(params) { params.url = params.url || 'no url'; var _error3 = false; var hasmirror = object$2.cub_mirrors.find(function (m) { return params.url.indexOf(m) >= 0; }); if (hasmirror) { // Заменияем протокол на живой урл params.url = params.url.replace('http://' + object$2.cub_domain, object$2.cub_alive).replace('https://' + object$2.cub_domain, object$2.cub_alive); var mirrors = object$2.cub_mirrors; Arrays.remove(mirrors, hasmirror); for (var name in bad_mirrors) { var mirror = bad_mirrors[name]; if (Date.now() - mirror.time > 1000 * 60 * 10) { mirror.time = Date.now(); mirror.urls = []; } if (mirror.urls.length > 10) { console.log('Request', 'bad mirror:', name, 'count:', mirror.urls.length); Arrays.remove(mirrors, name); mirror.urls = mirror.urls.slice(-11); } } _error3 = function error(jqXHR, exception) { if (mirrors.length > 0 && (jqXHR.status < 400 || jqXHR.error_time > 1000 * 15)) { if (!bad_mirrors[hasmirror]) bad_mirrors[hasmirror] = { urls: [], time: Date.now() }; if (bad_mirrors[hasmirror].urls.indexOf(params.url) < 0) { bad_mirrors[hasmirror].urls.push(params.url); } var next = mirrors.shift(); console.log('Request', 'try next mirror for:', params.url, 'next mirror:', next); object$2.cub_mirrors.forEach(function (mirror) { params.url = params.url.replace(mirror, next); }); hasmirror = next; request(params, _error3); } else { if (params.before_error) params.before_error(jqXHR, exception); if (params.error) params.error(jqXHR, exception); if (params.after_error) params.after_error(jqXHR, exception); if (params.end) params.end(); } }; } request(params, _error3); } function cacheGet(params, callback) { if (params.cache && params.cache.life > 0 && Storage.field('request_caching')) { Cache.getData('other', cacheName(params), -1, true).then(function (result) { if (result) { if (Date.now() < result.time + params.cache.life * 1000 * 60) callback(result.value, result.value);else callback(null, result.value); } else callback(null, null); })["catch"](function (e) { callback(null, null); }); } else callback(null, null); } function cacheSet(params, data) { Cache.rewriteData('other', cacheName(params), data)["catch"](function (e) {}); } function cacheName(params) { var url = params.url || ''; // убираем зеркало из урла, что бы не было дублей в кеше object$2.cub_mirrors.forEach(function (mirror) { url = url.replace(mirror, ''); }); url = url.replace(/https?:\/\//i, ''); return 'request_[' + url + '][' + JSON.stringify(params.post_data || {}) + (params.dataType || 'json') + Storage.field('tmdb_lang') + ']'; } /** * Сделать запрос * @param {Object} params */ function request(params) { var errorCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; Lampa.Listener.send('request_before', { params: params }); var start_time = Date.now(); var cache_old = false; var error = function error(jqXHR, exception) { if (params.attempts && params.attempts > 0) { params.attempts--; console.log('Request', 'attempt left:', params.attempts, 'for', params.url); return go(params); } // Если есть старый кеш отдаем его if (cache_old) { console.log('Request', 'use old cache for:', params.url, 'key:', cacheName(params)); return secuses(cache_old, true); } jqXHR.decode_error = errorDecode(jqXHR, exception); jqXHR.decode_code = errorCode(jqXHR); params.url = params.url || 'no url'; Lampa.Listener.send('request_error', { params: params, error: jqXHR, exception: exception }); var end_time = Date.now() - start_time; var time = end_time > 1000 ? Math.round(end_time / 1000) + 's' : end_time + 'ms'; jqXHR.error_time = time; console.log('Request', params.post_data ? 'POST' : 'GET', 'time:', time, 'error of ' + params.url + ' :', errorDecode(jqXHR, exception)); if (errorCallback) return errorCallback(jqXHR, exception); if (params.before_error) params.before_error(jqXHR, exception); if (params.error) params.error(jqXHR, exception); if (params.after_error) params.after_error(jqXHR, exception); if (params.end) params.end(); }; if (typeof params.url !== 'string' || !params.url) return error({ status: 404 }, ''); listener.send('go'); last_reguest = params; if (params.start) params.start(); var secuses = function secuses(data) { var fromcache = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; function sendSecuses(send_data) { if (params.cache && params.cache.life > 0 && !fromcache) { cacheSet(params, send_data); } if (params.before_complite) params.before_complite(send_data); if (params.complite) { try { params.complite(send_data); } catch (e) { console.error('Request', 'complite error:', e.message + "\n\n" + e.stack); Noty.show('Error: ' + (e.error || e).message + '

    ' + (e.error && e.error.stack ? e.error.stack : e.stack || '').split("\n").join('
    ')); } } if (params.after_complite) params.after_complite(send_data); if (params.end) params.end(); } var abort_called = false; Lampa.Listener.send('request_secuses', { params: params, data: data, abort: function abort() { abort_called = true; return sendSecuses; } }); if (!abort_called) sendSecuses(data); }; var datatype = params.dataType || 'json'; var timeout = !Mirrors.connected() && params.url.indexOf(object$2.cub_domain) >= 0 ? 2000 : params.timeout || need.timeout; var data = { dataType: datatype, url: params.url, timeout: timeout, crossDomain: true, success: function success(data) { if (datatype == 'json' && !data) error({ status: 500 });else secuses(data); }, error: error, beforeSend: function beforeSend(xhr) { var use = Storage.field('torrserver_auth'); var srv = Storage.get(Storage.field('torrserver_use_link') == 'two' ? 'torrserver_url_two' : 'torrserver_url'); if (use && srv && params.url.indexOf(srv) >= 0) { var authorization = "Basic " + Base64.encode(Storage.get('torrserver_login') + ':' + Storage.value('torrserver_password')); xhr.setRequestHeader("Authorization", authorization); } if (params.beforeSend) { xhr.setRequestHeader(params.beforeSend.name, params.beforeSend.value); } } }; if (params.withCredentials) { data.xhrFields = { withCredentials: true }; } if (params.post_data) { data.type = 'POST'; data.data = params.post_data; } if (params.type) data.type = params.type; if (params.headers) { data.headers = params.headers; } cacheGet(params, function (cached, old) { // Запомнить что есть старый кеш на случай ошибки что бы отдать его cache_old = old; if (cached) { secuses(cached, true); } else { $.ajax(data); } }); need.timeout = 1000 * 30; } /** * Сделать нативный Android запрос * @param {Object} params */ function android_go(params) { Lampa.Listener.send('request_before', { params: params }); var start_time = Date.now(); var error = function error(jqXHR, exception) { Lampa.Listener.send('request_error', { params: params, error: jqXHR }); var end_time = Date.now() - start_time; var time = end_time > 1000 ? Math.round(end_time / 1000) + 's' : end_time + 'ms'; console.log('Request', params.post_data ? 'POST' : 'GET', 'time:', time, 'error of ' + params.url + ' :', errorDecode(jqXHR, exception)); if (params.before_error) params.before_error(jqXHR, exception); if (params.error) params.error(jqXHR, exception); if (params.after_error) params.after_error(jqXHR, exception); if (params.end) params.end(); }; if (typeof params.url !== 'string' || !params.url) return error({ status: 404 }, ''); listener.send('go'); last_reguest = params; if (params.start) params.start(); var secuses = function secuses(data) { Lampa.Listener.send('request_secuses', { params: params, data: data }); if (params.before_complite) params.before_complite(data); if (params.complite) { try { params.complite(data); } catch (e) { console.error('Request', 'complite error:', e.message + "\n\n" + e.stack); Noty.show('Error: ' + (e.error || e).message + '

    ' + (e.error && e.error.stack ? e.error.stack : e.stack || '').split("\n").join('
    ')); } } if (params.after_complite) params.after_complite(data); if (params.end) params.end(); }; params.timeout = !Mirrors.connected() && params.url.indexOf(object$2.cub_domain) >= 0 ? 3000 : params.timeout || need.timeout; Android.httpReq(params, { complite: secuses, error: error }); need.timeout = 1000 * 30; } function _native(params) { if (Platform.is('android')) android_go(params);else go(params); } } /** * Прогресс выполнения нескольких асинхронных задач * @example * let progress = new Progress() * progress.append((done)=>{ // какая-то асинхронная задача * done(data) // по завершении вызываем done с результатом * }) * progress.append([ // можно добавить массив задач одним вызовом * (done)=>{ // какая-то асинхронная задача * done(data) // по завершении вызываем done с результатом * }, * (done)=>{ // какая-то асинхронная задача * done(data) // по завершении вызываем done с результатом * } * ]) * progress.start((result)=>{ // запуск всех задач * console.log(result) // массив результатов всех задач * }) */ function Progress() { var works = []; var result = []; var loaded = 0; this.append = function (call) { if (Arrays.isArray(call)) works = works.concat(call);else if (typeof call == 'function') works.push(call); }; this.start = function (complite) { works.forEach(function (fun, i) { try { fun(function (data) { result[i] = data; loaded++; if (loaded == works.length) complite(result); }); } catch (e) { console.warn('Progress', 'task error:', e.message, e.stack); loaded++; if (loaded == works.length) complite(result); } }); }; } var network$4 = new Request(); var day = 60 * 24; var source$1 = 'cub'; function url(u) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var genre = params.genres; if (permit.child_small) genre = genre ? genre + (genre == '16' ? '' : ',16') : '16'; if (genre && u.indexOf('genre') == -1) u = add$7(u, 'genre=' + genre); if (params.page) u = add$7(u, 'page=' + params.page); if (params.query) u = add$7(u, 'query=' + params.query); if (params.filter) { for (var i in params.filter) { u = add$7(u, i + '=' + params.filter[i]); } } var email = Storage.get('account', '{}').email || ''; return Utils$1.addUrlComponent(Utils$1.protocol() + 'tmdb.' + object$2.cub_domain + '/' + u, 'email=' + encodeURIComponent(email)); } function add$7(u, params) { return u + (/\?/.test(u) ? '&' : '?') + params; } function get$7(method) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var oncomplite = arguments.length > 2 ? arguments[2] : undefined; var onerror = arguments.length > 3 ? arguments[3] : undefined; var cache = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; var u = url(method, params); network$4.silent(u, function (json) { json.url = method; json.source = source$1; // Фильтруем результаты по ключевым словам, чтобы не показывать фильмы с неуместными словами в названии if (json.results && Arrays.isArray(json.results)) { json.results = json.results.filter(function (item) { var title = (item.title || item.name || '').toLowerCase(); return !Keys.filter.find(function (word) { var reg = new RegExp(word, 'i'); return reg.test(title); }); }); } oncomplite(Utils$1.addSource(json, source$1)); }, onerror, false, { cache: cache }); } function list$3() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var u = url(params.url, params); network$4.silent(u, function (data) { oncomplite(Utils$1.addSource(data, source$1)); }, onerror, false, { cache: { life: day * 2 } }); } function main$1() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var parts_limit = 6; var parts_data = [function (call) { get$7('?sort=now_playing', params, function (json) { json.title = Lang.translate('title_now_watch'); call(json); }, call, { life: day * 2 }); }, function (call) { get$7('?sort=latest', params, function (json) { json.title = Lang.translate('title_latest'); call(json); }, call, { life: day * 2 }); }, function (call) { if (permit.child_small) return call(); get$7('top/fire/movie', params, function (json) { json.title = Lang.translate('title_fire'); json.icon_svg = Template.string('icon_fire'); json.icon_bgcolor = '#fff'; json.icon_color = '#fd4518'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day * 7 }); }, function (call) { get$7('?cat=' + params.url + '&sort=latest&uhd=true', params, function (json) { json.title = Lang.translate('title_in_high_quality'); json.results.forEach(function (card) { card.params = { style: { name: 'wide' } }; }); json.params = { items: { view: 3 } }; call(json); }, call, { life: day * 3 }); }, function (call) { if (permit.child_small) return call(); get$7('top/hundred/movie', params, function (json) { json.title = Lang.translate('title_top_100') + ' - ' + Lang.translate('menu_movies'); json.icon_svg = Template.string('icon_top'); json.icon_bgcolor = '#e02129'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day * 7 }); }, function (call) { if (permit.child_small) return call(); get$7('top/hundred/tv', params, function (json) { json.title = Lang.translate('title_top_100') + ' - ' + Lang.translate('menu_tv'); json.icon_svg = Template.string('icon_top'); json.icon_bgcolor = '#e02129'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day * 7 }); }, function (call) { if (permit.child) return call(); trailers('added', call); }]; ContentRows.call('main', params, parts_data); var start_shuffle = parts_data.length + 1; if (!permit.child_small) Arrays.insert(parts_data, 0, Api.partPersons(parts_data, parts_limit, 'movie', start_shuffle)); TMDB.genres.movie.filter(function (a) { return !(a.id == 10763 || a.id == 10767); }).forEach(function (genre) { var event = function event(call) { get$7('?sort=top&genre=' + genre.id + (permit.child_small ? ',16' : ''), params, function (json) { json.title = Lang.translate(genre.title.replace(/[^a-z_]/g, '')); call(json); }, call, { life: day * 7 }); }; parts_data.push(event); }); if (!permit.child_small) { network$4.silent(Utils$1.protocol() + object$2.cub_domain + '/api/collections/list?category=new', function (data) { data.results.forEach(function (collection, index) { var event = function event(call_inner) { get$7('collections/' + collection.id, {}, function (json) { json.title = Utils$1.capitalizeFirstLetter(collection.title); json.icon_svg = Template.string('icon_collection'); json.icon_color = '#fff'; json.icon_bgcolor = 'rgba(255,255,255,0.15)'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call_inner(json); }, call_inner, { life: day * 3 }); }; parts_data.push(event); }); Arrays.shuffleArrayFromIndex(parts_data, start_shuffle); }, false, false, { cache: { life: day * 3 } }); } function loadPart(partLoaded, partEmpty) { Api.partNext(parts_data, parts_limit, partLoaded, partEmpty); } loadPart(oncomplite, onerror); return loadPart; } function category$1() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var fullcat = !(params.genres || params.keywords); var airdate = params.url == 'anime' ? '&airdate=' + new Date().getFullYear() : ''; var years = [2000, 2010, 2015]; var parts_limit = 6; var parts_data = [function (call) { get$7('?cat=' + params.url + '&sort=now_playing' + airdate, params, function (json) { json.title = Lang.translate('title_now_watch'); call(json); }, call, { life: day * 2 }); }, function (call) { if (params.url == 'anime') { get$7('?cat=' + params.url + '&sort=top', params, function (json) { json.title = Lang.translate('title_in_top'); json.results.forEach(function (card) { card.params = { style: { name: 'wide' } }; }); json.params = { items: { view: 3 } }; call(json); }, call, { life: day * 7 }); } else { get$7('?cat=' + params.url + '&sort=latest&uhd=true' + airdate, params, function (json) { json.title = Lang.translate('title_in_high_quality'); json.results.forEach(function (card) { card.params = { style: { name: 'wide' } }; }); json.params = { items: { view: 3 } }; call(json); }, call, { life: day * 3 }); } }, function (call) { if (params.url == 'tv' || params.url == 'anime') { get$7('?cat=' + params.url + '&sort=airing' + airdate, params, function (json) { json.title = Lang.translate('title_ongoing'); call(json); }, call, { life: day * 2 }); } else call(); }, function (call) { get$7('?cat=' + params.url + '&sort=top' + airdate, params, function (json) { json.title = Lang.translate('title_popular'); call(json); }, call, { life: day * 3 }); }, function (call) { get$7('?cat=' + params.url + '&sort=now&airdate=' + new Date().getFullYear(), params, function (json) { json.title = Lang.translate('title_new_this_year'); call(json); }, call, { life: day * 2 }); }, function (call) { if (params.url == 'anime' || !fullcat || permit.child_small) call();else { get$7('top/fire/' + params.url, params, function (json) { json.title = Lang.translate('title_fire'); json.icon_svg = Template.string('icon_fire'); json.icon_bgcolor = '#fff'; json.icon_color = '#fd4518'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day * 7 }); } }, function (call) { if (params.url == 'anime' || !fullcat || permit.child_small) call();else { get$7('top/hundred/' + params.url, params, function (json) { json.title = Lang.translate('title_top_100'); json.icon_svg = Template.string('icon_top'); json.icon_bgcolor = '#e02129'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day * 7 }); } }, function (call) { if (params.url == 'movie' && fullcat && !permit.child) trailers('added', call);else call(); }, function (call) { get$7('?cat=' + params.url + '&sort=top&airdate=' + (new Date().getFullYear() - 1), params, function (json) { json.title = Lang.translate('title_last_year'); call(json); }, call, { life: day * 7 }); }, function (call) { get$7('?cat=' + params.url + '&sort=top&airdate=' + (new Date().getFullYear() - 7) + '-' + (new Date().getFullYear() - 2) + '&vote=6-8', params, function (json) { json.title = Lang.translate('title_worth_rewatch'); call(json); }, call, { life: day * 7 }); }, function (call) { get$7('?cat=' + params.url + '&sort=top&airdate=' + (new Date().getFullYear() - 7) + '-' + (new Date().getFullYear() - 2) + '&vote=8-10', params, function (json) { json.title = Lang.translate('title_hight_voite'); json.icon_svg = Template.string('icon_star'); json.icon_bgcolor = '#fff'; json.icon_color = '#212121'; json.params = { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon') }; call(json); }, call, { life: day * 7 }); }]; // Дух Рождества // if(params.url !== 'anime'){ // Arrays.insert(parts_data, 1, Api.partKeyword({id: 207317, title: '#{filter_keyword_christmas}'}, params.url, params)) // } ContentRows.call('category', params, parts_data); var start_shuffle = parts_data.length + 2; if (params.url == 'anime') { Arrays.insert(parts_data, 0, Api.partKeywords(parts_data, 'tv', start_shuffle, [], { genres: 16, orig_lang: 'ja' })); } else { Arrays.insert(parts_data, 0, Api.partKeywords(parts_data, params.url, start_shuffle, [], permit.child_small ? { genres: 16 } : params)); } if (fullcat && !permit.child_small) { Arrays.insert(parts_data, 0, Api.partPersons(parts_data, parts_limit + 3, params.url, start_shuffle)); } if (TMDB.genres[params.url] && fullcat) { TMDB.genres[params.url].filter(function (a) { return !(a.id == 10763 || a.id == 10767); }).forEach(function (genre) { var event = function event(call) { get$7('?cat=' + params.url + '&sort=top&genre=' + genre.id + (permit.child_small ? ',16' : ''), params, function (json) { json.title = Lang.translate(genre.title.replace(/[^a-z_]/g, '')); call(json); }, call, { life: day * 7 }); }; parts_data.push(event); }); } else if (params.url == 'anime') { TMDB.genres.tv.filter(function (a) { return !(a.id == 99 || a.id == 10766); }).forEach(function (genre) { var event = function event(call) { get$7('?cat=' + params.url + '&sort=top&genre=' + genre.id + (permit.child_small ? ',16' : ''), params, function (json) { json.title = Lang.translate(genre.title.replace(/[^a-z_]/g, '')); call(json); }, call, { life: day * 7 }); }; parts_data.push(event); }); } years.forEach(function (year) { var eventYear = function eventYear(call) { get$7('?cat=' + params.url + '&sort=top&airdate=' + year + '-' + (year + 5), params, function (json) { json.title = Lang.translate('title_best_of_' + year); call(json); }, call, { life: day * 7 }); }; parts_data.push(eventYear); var eventComedy = function eventComedy(call) { get$7('?cat=' + params.url + '&sort=top&airdate=' + year + '-' + (year + 5) + '&genre=35' + (permit.child_small ? ',16' : ''), params, function (json) { json.title = Lang.translate('title_comedy_of_' + year); call(json); }, call, { life: day * 7 }); }; if (fullcat) parts_data.push(eventComedy); }); Arrays.shuffleArrayFromIndex(parts_data, start_shuffle); function loadPart(partLoaded, partEmpty) { Api.partNext(parts_data, parts_limit, partLoaded, partEmpty); } loadPart(oncomplite, onerror); return loadPart; } function full$1(params, oncomplite, onerror) { var status = new Status(9); status.onComplite = oncomplite; if (Utils$1.dcma(params.method, params.id)) return onerror(); var image_language = (Storage.field('tmdb_lang') || 'en').split(/[-_]/)[0]; var image_languages = [image_language, 'en', 'null'].filter(function (language, index, list) { return list.indexOf(language) == index; }).join(','); get$7('3/' + params.method + '/' + params.id + '?api_key=' + TMDB$1.key() + '&append_to_response=content_ratings,release_dates,external_ids,keywords,alternative_titles,images&include_image_language=' + image_languages + '&language=' + Storage.field('tmdb_lang'), params, function (json) { var _json$overview; if (json.status_code) return status.stop(), onerror(); json.source = 'cub'; if (!((_json$overview = json.overview) !== null && _json$overview !== void 0 && _json$overview.trim()) && Storage.field('tmdb_lang') !== 'en') { TMDB.get(params.method + '/' + params.id, { langs: 'en' }, function (enjson) { var _enjson$overview; if ((_enjson$overview = enjson.overview) !== null && _enjson$overview !== void 0 && _enjson$overview.trim()) json.overview = enjson.overview; status.need--; status.check(); }, status.error.bind(status), { life: day * 7 }); } else status.need--; if (params.method == 'tv') { var season = Utils$1.countSeasons(json); TMDB.get('tv/' + json.id + '/season/' + season, {}, function (ep) { status.append('episodes', ep); }, status.error.bind(status), { life: day * 3 }); } else status.need--; if (json.belongs_to_collection) { TMDB.get('collection/' + json.belongs_to_collection.id, {}, function (collection) { collection.results = collection.parts.slice(0, 19); status.append('collection', Utils$1.addSource(collection, 'tmdb')); }, status.error.bind(status), { life: day * 7 }); } else status.need--; status.append('movie', json); }, function () { status.need -= 2; status.error(); }, { life: day * 7 }); if (!permit.child_small) { TMDB.get(params.method + '/' + params.id + '/credits', params, function (json) { status.append('persons', json); }, status.error.bind(status), { life: day * 7 }); TMDB.get(params.method + '/' + params.id + '/recommendations', params, function (json) { status.append('recomend', json); }, status.error.bind(status), { life: day * 7 }); TMDB.get(params.method + '/' + params.id + '/similar', params, function (json) { status.append('simular', json); }, status.error.bind(status), { life: day * 7 }); if (!permit.child) { TMDB.videos(params, function (json) { status.append('videos', json); }, status.error.bind(status)); } else { status.need -= 1; } } else { status.need -= 4; } reactionsGet(params, function (json) { status.append('reactions', json); }); if (Lang.selected(['ru', 'uk', 'be']) && window.lampa_settings.account_use && !permit.child) { status.need++; discussGet(params, function (json) { status.append('discuss', json); }, status.error.bind(status)); } if (params.method == 'movie') { status.need++; metadataGet(params, function (json) { status.append('metadata', json); }, status.error.bind(status)); } } function trailers(type, oncomplite) { network$4.silent(Utils$1.protocol() + object$2.cub_domain + '/api/trailers/short/trailers/' + type, function (result) { result.title = Lang.translate('title_trailers') + ' - ' + Lang.translate('title_new'); result.results.forEach(function (card) { card.params = { style: { name: 'wide' } }; }); oncomplite(Utils$1.addSource(result, source$1)); }, function () { oncomplite({ results: [] }); }, false, { cache: { life: day * 2 } }); } function metadataGet(params, oncomplite) { if (window.lampa_settings.disable_features.metadata) return oncomplite({}); network$4.silent(Utils$1.protocol() + object$2.cub_domain + '/api/ai/video-view/' + params.id + '/metadata?type=' + params.method, oncomplite, function () { oncomplite({}); }, false, { timeout: 1000 * 5 }); } function reactionsGet(params, oncomplite) { if (window.lampa_settings.disable_features.reactions) return oncomplite({ result: [] }); network$4.silent(Utils$1.protocol() + object$2.cub_domain + '/api/reactions/get/' + params.method + '_' + params.id, oncomplite, function () { oncomplite({ result: [] }); }, false, { timeout: 1000 * 5 }); } function discussGet(params, oncomplite, onerror) { if (window.lampa_settings.disable_features.discuss) return onerror(); network$4.silent(Utils$1.protocol() + object$2.cub_domain + '/api/discuss/get/' + params.method + '_' + params.id + '/' + (params.page || 1) + '/' + Storage.field('language'), oncomplite, onerror, false, { timeout: 1000 * 5 }); } function reactionsAdd(params, oncomplite, onerror) { network$4.silent(Utils$1.protocol() + object$2.cub_domain + '/api/reactions/add/' + params.method + '_' + params.id + '/' + params.type + '?uid=' + Storage.get('lampa_uid', 'none'), oncomplite, onerror); } function menuCategory$1(params, oncomplite) { var menu = []; menu.push({ title: Lang.translate('title_now_watch'), url: '?cat=' + params.action + '&sort=now_playing' }); if (params.action == 'tv') { menu.push({ title: Lang.translate('title_new_episodes'), url: '?cat=' + params.action + '&sort=update' }); } menu.push({ title: Lang.translate('title_popular'), url: '?cat=' + params.action + '&sort=top' }); menu.push({ title: Lang.translate('title_latest'), url: '?cat=' + params.action + '&sort=latest' }); menu.push({ title: Lang.translate('title_new_this_year'), url: '?cat=' + params.action + '&sort=now' }); menu.push({ title: Lang.translate('title_hight_voite'), url: '?cat=' + params.action + '&sort=latest&vote=7' }); oncomplite(menu); } function search$3() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var status = new Status(3); status.onComplite = function (data) { var items = []; if (data.movie && data.movie.results.length) items.push(data.movie); if (data.tv && data.tv.results.length) items.push(data.tv); if (data.anime && data.anime.results.length) items.push(data.anime); oncomplite(items); }; get$7('search/movie', params, function (json) { json.title = Lang.translate('menu_movies'); json.type = 'movie'; status.append('movie', json); }, status.error.bind(status)); get$7('search/tv', params, function (json) { json.title = Lang.translate('menu_tv'); json.type = 'tv'; status.append('tv', json); }, status.error.bind(status)); get$7('search/anime', params, function (json) { json.title = Lang.translate('menu_anime'); json.type = 'anime'; status.append('anime', json); }, status.error.bind(status)); } function discovery$1() { return { title: 'CUB', search: search$3, params: { save: true }, onMore: function onMore(params, close) { close(); Activity.push({ url: 'search/' + params.data.type, title: Lang.translate('search') + ' - ' + params.query, component: 'category_full', page: 2, query: encodeURIComponent(params.query), source: 'cub' }); }, onCancel: network$4.clear.bind(network$4) }; } function extensions$1(call) { var headers = {}; if (permit.token && window.lampa_settings.account_use) { headers = { headers: { token: permit.token, profile: permit.account.profile.id } }; } network$4.timeout(5000); network$4.silent(Utils$1.protocol() + object$2.cub_domain + '/api/extensions/list', function (result) { if (result.secuses) { Storage.set('account_extensions', result); call(result); } else { call(Storage.get('account_extensions', '{}')); } }, function () { call(Storage.get('account_extensions', '{}')); }, false, headers); } function person$1(params, oncomplite, onerror) { TMDB.person(params, oncomplite, onerror); } function menu$1(params, oncomplite) { TMDB.menu(params, oncomplite); } function seasons$1(tv, from, oncomplite) { TMDB.seasons(tv, from, oncomplite); } function clear$5() { network$4.clear(); } var CUB = { main: main$1, menu: menu$1, full: full$1, list: list$3, category: category$1, clear: clear$5, person: person$1, seasons: seasons$1, menuCategory: menuCategory$1, discovery: discovery$1, reactionsGet: reactionsGet, reactionsAdd: reactionsAdd, metadataGet: metadataGet, discussGet: discussGet, extensions: extensions$1 }; /** * Источники */ var sources$1 = { tmdb: TMDB, cub: CUB }; /** * Чтоб не переписали их */ Object.defineProperty(sources$1, 'tmdb', { get: function get() { return TMDB; } }); Object.defineProperty(sources$1, 'cub', { get: function get() { return CUB; } }); var network$3 = new Request(); /** * Получить источник * @param {{source:string}} params * @returns {class} */ function source(params) { return params.source && sources$1[params.source] ? sources$1[params.source] : sources$1.tmdb; } function availableDiscovery() { var list = []; var active = Storage.get('source', 'tmdb'); for (var key in sources$1) { console.log('Api', 'discovery check:', key, sources$1[key].discovery ? true : false, _typeof(sources$1[key].discovery)); if (sources$1[key].discovery) { if (key === active) list.splice(0, 0, sources$1[key].discovery());else list.push(sources$1[key].discovery()); } } return list; } /** * Главная страница * @param {{source:string}} params * @param {function} oncomplite * @param {function} onerror */ function main() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; return source(params).main(params, oncomplite, onerror); } /** * Категория * @param {{url:string, source:string}} params * @param {function} oncomplite * @param {function} onerror */ function category() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; return source(params).category(params, oncomplite, onerror); } /** * Просмотр карточки * @param {{id:string, source:string, method:string, card:{}}} params * @param {function} oncomplite * @param {function} onerror */ function full() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; source(params).full(params, oncomplite, onerror); } /** * Главный поиск * @param {{query:string}} params * @param {function} oncomplite */ function search$2() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; TMDB.search(params, function (json) { var result = { movie: json.find(function (a) { return a.type == 'movie'; }), tv: json.find(function (a) { return a.type == 'tv'; }) }; oncomplite(result); }, function () { oncomplite({}); }); } /** * Что-то старое, надо проверить * @param {object} params * @param {function} oncomplite */ function menuCategory() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; source(params).menuCategory(params, oncomplite); } /** * Информация об персоне * @param {{id:integer, source:string}} params * @param {function} oncomplite * @param {function} onerror */ function person() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; source(params).person(params, oncomplite, onerror); } /** * Жанры * @param {object} params * @param {function} oncomplite * @param {function} onerror */ function genres() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; TMDB.genres(params, oncomplite, onerror); } /** * Компания * @param {{id:integer}} params * @param {function} oncomplite * @param {function} onerror */ function company() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; TMDB.company(params, oncomplite, onerror); } /** * Полная категори * @param {{page:integer, url:string, source:string}} params * @param {function} oncomplite * @param {function} onerror */ function list$2() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; source(params).list(params, oncomplite, onerror); } /** * Получить список категорий для каталога в меню * @param {{source:string}} params * @param {function} oncomplite */ function menu() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; source(params).menu(params, oncomplite); } /** * Сезоны * @param {{id:integer, source:string}} tv * @param {[1,2,3]} from - список сезонов 1,3,4... * @param {function} oncomplite */ function seasons(tv, from, oncomplite) { source(tv).seasons(tv, from, oncomplite); } /** * Коллекции * @param {object} params * @param {function} oncomplite * @param {function} onerror */ function collections(params, oncomplite, onerror) { source(params).collections(params, oncomplite, onerror); } /** * Закладки * @param {{page:integer, type:string}} params * @param {function} oncomplite * @param {function} onerror */ function favorite() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; var data = {}; data.results = Favorite.get(params); if (params.filter) { data.results = data.results.filter(function (a) { return params.filter == 'tv' ? a.name : !a.name; }); } data.total_pages = Math.ceil(data.results.length / 20); data.page = Math.min(params.page, data.total_pages); var offset = data.page - 1; data.results = data.results.slice(20 * offset, 20 * offset + 20); if (data.results.length) oncomplite(data);else onerror(); } /** * Релизы * @param {function} oncomplite * @param {function} onerror */ function reliseTmdbCard(card, oncomplite) { if (!card.imdb_id) return oncomplite(card); var method = 'find/' + card.imdb_id + '?external_source=imdb_id'; TMDB.get(method, {}, function (data) { var movie = data.movie_results && data.movie_results[0]; var tv = data.tv_results && data.tv_results[0]; var tmdb = card.name || card.original_name || card.first_air_date ? tv || movie : movie || tv; if (tmdb) { tmdb.source = 'tmdb'; tmdb.imdb_id = card.imdb_id; tmdb.imdb_rating = card.imdb_rating; tmdb.kp_rating = card.kp_rating; tmdb.release_quality = card.release_quality; } oncomplite(tmdb || card); }, function () { oncomplite(card); }, { life: 60 * 24 * 7 }); } function reliseTmdbCards(items, oncomplite) { var result = []; var index = 0; var active = 0; var limit = 4; function next() { if (index >= items.length && active == 0) return oncomplite(result); var _loop = function _loop() { var position = index; var card = items[index]; index++; active++; reliseTmdbCard(card, function (tmdb) { result[position] = tmdb; active--; next(); }); }; while (active < limit && index < items.length) { _loop(); } } next(); } function relise(params, oncomplite, onerror) { network$3.silent(Utils$1.protocol() + 'tmdb.' + object$2.cub_domain + '?sort=releases&results=20&page=' + params.page, function (data) { data = Utils$1.addSource(data, 'cub'); if (data.results && data.results.length) { reliseTmdbCards(data.results, function (results) { data.results = results; oncomplite(data); }); } else oncomplite(data); }, onerror); } function partKeywords(parts, type) { var shift = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var filter = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; var req_params = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; if (shift == 0) shift = parts.length; return function (call) { if (['movie', 'tv'].indexOf(type) == -1) return call(); call(); TMDB.keywords[type].filter(function (keyword) { return filter.indexOf(keyword.id) == -1; }).forEach(function (keyword) { parts.push(partKeyword(keyword, type, req_params)); }); Arrays.shuffleArrayFromIndex(parts, shift); }; } function partKeyword(keyword, type) { var req_params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return function (call) { var u = 'discover/' + type; u = Utils$1.addUrlComponent(u, 'with_keywords=' + keyword.id); if (req_params.orig_lang) u = Utils$1.addUrlComponent(u, 'with_original_language=' + req_params.orig_lang); if (req_params.genres && u.indexOf('with_genres') == -1) u = Utils$1.addUrlComponent(u, 'with_genres=' + req_params.genres); TMDB.get(u, {}, function (result) { result.title = Lang.translate(keyword.title); call(result); }, call, { life: 60 * 24 * 7 }); }; } /** * Части с популярными актерами/режиссерами * @param {array} parts * @param {number} parts_limit * @param {string} type 'movie' или 'tv' * @param {number} shift - сдвиг для перемешивания * @return {function} */ function partPersons(parts, parts_limit, type) { var shift = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; if (shift == 0) shift = parts.length; return function (call) { if (['movie', 'tv'].indexOf(type) == -1) return call(); TMDB.get('person/popular', {}, function (json) { call(); if (!json.results) return; json.results.sort(function (a, b) { return a.popularity - b.popularity; }); var filtred = json.results.filter(function (p) { return p.known_for_department && p.known_for; }); var persons = filtred.filter(function (p) { return (p.known_for_department || '').toLowerCase() == 'acting' && p.known_for.length; }).slice(0, 10); persons.forEach(function (person_data, index) { var event = function event(call_inner) { person({ only_credits: type, id: person_data.id }, function (result) { if (!result.credits) return call_inner(); var items = (result.credits[type] || []).filter(function (m) { return m.backdrop_path && m.vote_count > 20; }); if (type == 'tv') items = items.filter(function (m) { return !(m.genre_ids.indexOf(10767) >= 0 || m.genre_ids.indexOf(10763) >= 0); }); items.sort(function (a, b) { var da = a.release_date || a.first_air_date; var db = b.release_date || b.first_air_date; if (db > da) return 1;else if (db < da) return -1;else return 0; }); var src = person_data.profile_path ? TMDB.img(person_data.profile_path, 'w90_and_h90_face') : person_data.img || './img/actor.svg'; call_inner({ title: person_data.name, icon_img: src, results: items.length > 5 ? items.slice(0, 20) : [], params: { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon', 'More') } }); }); }; parts.push(event); }); Arrays.shuffleArrayFromIndex(parts, shift); }, call, { life: 60 * 24 * 3 }); }; } /** * Загрузка частей с ограничением * @param {array} parts * @param {number} parts_limit * @param {function} partLoaded * @param {function} partEmpty * @return {void} */ function partNext(parts, parts_limit, partLoaded, partEmpty) { var pieces = parts.filter(function (p) { return typeof p == 'function'; }).slice(0, 0 + parts_limit); if (pieces.length) { var progress = new Progress(); progress.append(pieces); progress.start(function (result) { var data = result.filter(function (r) { return r && r.results && r.results.length; }); for (var i = 0; i < pieces.length; i++) { parts[parts.indexOf(pieces[i])] = false; } if (data.length) { if (data.length < 3) { partNext(parts, parts_limit, function (more_data) { data = data.concat(more_data); partLoaded(data); }, function () { partLoaded(data); }); } else partLoaded(data); } else partNext(parts, parts_limit, partLoaded, partEmpty); }); } else partEmpty(); } /** * Очистить */ function clear$4() { for (var i in sources$1) { sources$1[i].clear(); } network$3.clear(); } function img() { return TMDB.img.apply(TMDB, arguments); } var Api = { main: main, img: img, full: full, list: list$2, genres: genres, category: category, search: search$2, clear: clear$4, company: company, person: person, favorite: favorite, seasons: seasons, screensavers: TMDB.screensavers, relise: relise, menu: menu, collections: collections, menuCategory: menuCategory, sources: sources$1, availableDiscovery: availableDiscovery, partPersons: partPersons, partNext: partNext, partKeyword: partKeyword, partKeywords: partKeywords }; var Main$1 = /*#__PURE__*/function (_Emit) { _inherits(Main, _Emit); var _super = _createSuper(Main); function Main(object) { var _this; _classCallCheck(this, Main); _this = _super.call(this); Arrays.extend(object, { params: {} }); _this.object = object; _this.params = object.params; _this.scroll = new Scroll({ mask: true, over: true, scroll_by_item: true, end_ratio: 2 }); _this.html = document.createElement('div'); _this.emit('init'); return _this; } _createClass(Main, [{ key: "create", value: function create() { this.activity.loader(true); this.scroll.minus(); this.html.append(this.scroll.render(true)); this.emit('create'); } }, { key: "empty", value: function empty(status) { this.scroll.nopadding(); this.emit('empty', status); this.activity.loader(false); this.activity.toggle(); } }, { key: "build", value: function build(data) { this.emit('build', data); this.activity.loader(false); this.activity.toggle(); } }, { key: "start", value: function start() { var _this2 = this; this.emit('start'); var controller = { link: this, toggle: function toggle() { _this2.scroll.restorePosition(); if (_this2.items.length && Platform.screen('tv')) _this2.items[_this2.active].toggle(); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else Controller.toggle('menu'); }, right: function right() { Navigator.move('right'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('head'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down'); }, back: this.emit.bind(this, 'back') }; this.emit('controller', controller); Controller.add('content', controller); Controller.toggle('content'); } }, { key: "pause", value: function pause() { this.emit('pause'); } }, { key: "resize", value: function resize() { this.emit('resize'); } }, { key: "render", value: function render(js) { return js ? this.html : $(this.html); } }, { key: "destroy", value: function destroy() { this.scroll.destroy(); this.html.remove(); this.emit('destroy'); } }]); return Main; }(Emit); var Items$1 = { onInit: function onInit() { this.items = []; this.loaded = []; this.active = 0; this.limit_view = Platform.screen('tv') ? 1 : 6; }, onDown: function onDown() { this.active++; this.active = Math.min(this.active, this.items.length - 1); this.items[this.active].toggle(); }, onUp: function onUp() { this.active--; if (this.active < 0) { this.active = 0; Controller.toggle('head'); } else { this.items[this.active].toggle(); } }, onAppend: function onAppend(item) { var _this = this; item.use({ onDown: this.emit.bind(this, 'down'), onUp: this.emit.bind(this, 'up'), onBack: this.emit.bind(this, 'back'), onLeft: this.emit.bind(this, 'left'), onActive: function onActive() { _this.active = _this.items.indexOf(item); }, onToggle: function onToggle() { _this.scroll.update(item.render(true)); } }); this.fragment.appendChild(item.render(true)); this.items.push(item); }, onPushLoaded: function onPushLoaded() { var add = this.loaded.shift(); if (add && add.length) { this.frament = document.createDocumentFragment(); add.forEach(this.emit.bind(this, 'createAndAppend')); this.scroll.append(this.fragment); Layer.visible(this.scroll.render(true)); } }, onResize: function onResize() { if (this.items[this.active]) this.scroll.update(this.items[this.active].render(true)); }, onCreate: function onCreate() { var _this2 = this; this.scroll.onWheel = function (step) { _this2.emit(step > 0 ? 'down' : 'up'); }; this.scroll.onAnimateEnd = function () { _this2.emit('pushLoaded'); }; }, onBuild: function onBuild(data) { this.fragment = document.createDocumentFragment(); data.forEach(this.emit.bind(this, 'createAndAppend')); this.scroll.append(this.fragment); Layer.visible(this.scroll.render(true)); }, onDestroy: function onDestroy() { Arrays.destroy(this.items); } }; var Base$1 = /*#__PURE__*/function (_Emit) { _inherits(Base, _Emit); var _super = _createSuper(Base); function Base(data) { var _this; _classCallCheck(this, Base); _this = _super.call(this); Arrays.extend(data, { params: {} }); Arrays.extend(data.params, { type: 'default', items: { mapping: 'line', align_left: false, view: 7 }, scroll: { horizontal: true, step: 300 } }); _this.data = data; _this.params = data.params; _this.html = Template.js('items_line', data); _this.body = _this.html.find('.items-line__body'); _this.scroll = new Scroll(data.params.scroll); return _this; } _createClass(Base, [{ key: "create", value: function create() { var _this$html$find; this.scroll.onWheel = this.wheel.bind(this); this.html.addClass('items-line--type-' + this.params.type); if (!this.data.title && !this.has(Icon$1)) (_this$html$find = this.html.find('.items-line__head')) === null || _this$html$find === void 0 ? void 0 : _this$html$find.remove(); this.html.on('visible', this.visible.bind(this)); this.body.append(this.scroll.render(true)); this.emit('create'); } }, { key: "wheel", value: function wheel(step) { if (!Controller.own(this)) this.toggle(); Controller.enabled().controller[step > 0 ? 'right' : 'left'](); this.emit('wheel'); } }, { key: "visible", value: function visible() { this.emit('visible'); Layer.visible(this.scroll.render(true)); } }, { key: "toggle", value: function toggle() { var _this2 = this; var controller = { link: this, toggle: function toggle() { Controller.collectionSet(_this2.scroll.render(true)); Controller.collectionFocus(_this2.last || false, _this2.scroll.render(true)); _this2.emit('toggle'); }, right: function right() { if (Navigator.canmove('right')) Navigator.move('right'); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else _this2.emit('left'); }, down: this.emit.bind(this, 'down'), up: this.emit.bind(this, 'up'), back: this.emit.bind(this, 'back') }; this.emit('controller', controller); Controller.add('items_line', controller); Controller.toggle('items_line'); } }, { key: "render", value: function render(js) { return js ? this.html : $(this.html); } }, { key: "destroy", value: function destroy() { this.scroll.destroy(); this.html.remove(); this.emit('destroy'); } }]); return Base; }(Emit); var Line$4 = /*#__PURE__*/function (_Base) { _inherits(Line, _Base); var _super = _createSuper(Line); function Line(data) { var _this; _classCallCheck(this, Line); _this = _super.call(this, data); var module = typeof _this.params.module !== 'undefined' ? _this.params.module : Helper$b.MASK.base; Helper$b.getNames(module).map(function (name) { return LineMap[name]; }).forEach(function (mod) { return _this.use(mod); }); _this.emit('init'); return _this; } return Line; }(Base$1); var Create$1 = { onCreateAndAppend: function onCreateAndAppend(element) { try { var item = Utils$1.createInstance(Line$4, element); this.emit('instance', item, element); item.create(); this.emit('append', item, element); } catch (e) { console.warn('Warning', 'onCreateAndAppend error:', e.message, e.stack); } } }; var Callback = { onLeft: function onLeft() { Controller.toggle('menu'); }, onBack: function onBack() { Activity.backward(); } }; var Next$1 = { onInit: function onInit() { this.next_wait; this.builded_time = Date.now(); }, onBuild: function onBuild() { this.scroll.onEnd = this.emit.bind(this, 'loadNext'); this.builded_time = Date.now(); }, onLoadNext: function onLoadNext() { var _this = this; if (!this.next_wait && this.items.length && this.builded_time < Date.now() - 1000) { this.next_wait = true; this.emit('next', function (new_data) { _this.next_wait = false; if (!_this.items.length || _this.destroyed) return; var split_total = Math.ceil(new_data.length / _this.limit_view); // Разбиваем на части, чтобы не лагал браузер for (var i = 0; i < split_total; i++) { _this.loaded.push(new_data.slice(i * _this.limit_view, (i + 1) * _this.limit_view)); } // Если нет анимации у скрола, то можно грузить сразу if (!_this.scroll.animated()) _this.scroll.onAnimateEnd(); }, function () { _this.next_wait = false; }); } } }; var Item = /*#__PURE__*/function () { function Item(data, params) { _classCallCheck(this, Item); this.data = data; this.params = params; } _createClass(Item, [{ key: "create", value: function create() { this.html = Template.js(this.template, {}); this.html.addEventListener('visible', this.visible.bind(this)); } }, { key: "update", value: function update() { var _this = this; var url = (this.data.url || this.data.link) + ''; var loc = url.slice(0, 6) == 'https:' || window.location.protocol == 'https:'; var hts = url.slice(0, 6) == 'https:' || url.slice(0, 5) == 'http:'; if (Plugins.errors().find(function (s) { return s == (_this.data.url || _this.data.link); })) { var erl = this.html.querySelector('.extensions__item-error'); if (erl) erl.toggleClass('hide', false); } if (window.location.protocol == 'https:' && hts) url = url.replace(/^(http:\/\/|https:\/\/)/, 'https://'); this.html.querySelector('.extensions__item-name').innerText = this.data.name || Lang.translate('extensions_no_name'); this.html.querySelector('.extensions__item-author').innerText = this.data.author || (this.params.type == 'plugins' ? '@cub' : '@lampa'); this.html.querySelector('.extensions__item-descr').innerText = (this.data.descr || url).replace(/\n|\t|\r/g, ' '); var proto = this.html.querySelector('.extensions__item-proto'); if (proto && hts) { proto.toggleClass('hide', !Boolean(this.params.type == 'plugins' || this.params.type == 'installs')); proto.addClass('protocol-' + (loc ? 'https' : 'http')); } var status = this.html.querySelector('.extensions__item-disabled'); status.innerText = Lang.translate('player_disabled'); if (this.data.status || !this.params.autocheck) status.classList.add('hide');else status.classList.remove('hide'); } }, { key: "cub", value: function cub() { var cub = document.createElement('div'); cub.classList.add('extensions__cub'); cub.innerText = 'CUB'; this.html.appendChild(cub); } }, { key: "premium", value: function premium() { var author = this.html.querySelector('.extensions__item-author'); var premium = document.createElement('span'); premium.classList.add('extensions__item-premium'); premium.innerText = 'CUB Premium'; var where = author || this.html; where.appendChild(premium); } }, { key: "visible", value: function visible() { this.update(); } }, { key: "render", value: function render() { return this.html; } }, { key: "destroy", value: function destroy() { this.html.remove(); } }]); return Item; }(); function showReload(cancel) { Modal.open({ title: '', align: 'center', zIndex: 300, html: $('
    ' + Lang.translate('plugins_need_reload') + '
    '), buttons: [{ name: Lang.translate('settings_param_no'), onSelect: function onSelect() { Modal.close(); cancel(); } }, { name: Lang.translate('settings_param_yes'), onSelect: function onSelect() { window.location.reload(); } }] }); } function showInfo(plug, back) { var modal = Template.get('extensions_info'); var footer = $('.extensions-info__footer', modal); if (plug.image) modal.prepend($('')); $('.extensions-info__descr', modal).text(plug.descr); $('.extensions-info__instruction', modal).html((plug.instruction || Lang.translate('extensions_no_info')).replace(/\n/g, '
    ').replace(/\s\s/g, '  ')); function addLabel(name, value) { var label = $("
    \n
    ".concat(name, "
    \n
    ").concat(value, "
    \n
    ")); footer.append(label); } if (plug.link) addLabel(Lang.translate('settings_parser_jackett_link'), plug.link); if (plug.author) addLabel(Lang.translate('title_author'), plug.author); if (plug.time) addLabel(Lang.translate('settings_added'), Utils$1.parseTime(plug.time).full); Modal.open({ title: plug.name || Lang.translate('extensions_info'), html: modal, size: 'large', onBack: function onBack() { Modal.close(); back(); } }); } var Utils = { showReload: showReload, showInfo: showInfo }; var Extension = /*#__PURE__*/function (_Item) { _inherits(Extension, _Item); var _super = _createSuper(Extension); function Extension(data, params) { var _this; _classCallCheck(this, Extension); _this = _super.call(this, data, params); _this.network = new Request(); _this.template = 'extensions_item'; return _this; } _createClass(Extension, [{ key: "visible", value: function visible() { _get(_getPrototypeOf(Extension.prototype), "visible", this).call(this); if (this.params.cub) this.cub(); if (this.data.premium) this.premium(); if (this.params.autocheck) this.check(); if (Plugins.loaded().indexOf(this.data.url || this.data.link) >= 0) this.html.querySelector('.extensions__item-included').classList.remove('hide'); this.html.addEventListener('hover:enter', this.menu.bind(this)); } }, { key: "menu", value: function menu() { var _this2 = this; if (this.removed) return; var menu = []; var controller = Controller.enabled().name; var back = function back() { Controller.toggle(controller); }; if (this.params.type == 'plugins' || this.params.type == 'installs') { menu.push({ title: Lang.translate('extensions_' + (this.data.status ? 'disable' : 'enable')), toggle: true }); } menu.push({ title: Lang.translate('extensions_check'), status: true }); if (this.params.cub || this.params.noedit) { if (this.params.type == 'extensions') { menu.push({ title: Lang.translate('extensions_install'), install: true }); } menu.push({ title: Lang.translate('extensions_info'), instruction: true }); } else { menu.push({ title: Lang.translate('extensions_edit'), separator: true }); menu.push({ title: Lang.translate('extensions_change_name'), change: 'name' }); menu.push({ title: Lang.translate('extensions_change_link'), change: 'url' }); menu.push({ title: Lang.translate('extensions_remove'), remove: true }); } Select.show({ title: Lang.translate('title_action'), items: menu, onBack: back, onSelect: function onSelect(a) { if (a.toggle) { _this2.data.status = _this2.data.status == 1 ? 0 : 1; if (_this2.params.cub) Account$1.Api.pluginToggle(_this2.data, _this2.data.status);else Plugins.save(_this2.data); _this2.update(); if (_this2.data.status == 1) { back(); Plugins.push(_this2.data); } else { Utils.showReload(back); } } else if (a.change) { Input.edit({ title: a.change == 'name' ? Lang.translate('extensions_set_name') : Lang.translate('extensions_set_url'), value: _this2.data[a.change] || '', free: true, nosave: true }, function (new_value) { if (new_value) { _this2.data[a.change] = new_value; Plugins.save(_this2.data); _this2.update(); if (a.change == 'url') { _this2.check(); Plugins.push(_this2.data); } } back(); }); } else if (a.status) { back(); _this2.check(); } else if (a.install) { var ready = Plugins.get().find(function (b) { return b.url == _this2.data.link; }); if (ready) { Noty.show(Lang.translate('extensions_ready')); back(); } else { back(); Plugins.add({ url: _this2.data.link, status: 1, name: _this2.data.name, author: _this2.data.author }); _this2.html.querySelector('.extensions__item-included').classList.remove('hide'); } } else if (a.instruction) { Utils.showInfo(_this2.data, back); } else if (a.remove) { Plugins.remove(_this2.data); _this2.html.style.opacity = 0.5; _this2.removed = true; Utils.showReload(back); } } }); } }, { key: "check", value: function check() { var check = this.html.querySelector('.extensions__item-check'); var code = this.html.querySelector('.extensions__item-code'); var stat = this.html.querySelector('.extensions__item-status'); check.classList.remove('hide'); code.classList.add('hide'); stat.classList.add('hide'); var display = function display(type, num, text) { code.innerText = num; code.classList.remove('hide'); code.classList.remove('success'); code.classList.remove('error'); code.classList.add(type); stat.innerText = text; stat.classList.remove('hide'); check.classList.add('hide'); }; var url = Utils$1.fixMirrorLink(Utils$1.rewriteIfHTTPS(this.data.url || this.data.link)); this.network.timeout(5000); this.network["native"](url, function (str) { if (/Lampa\./.test(str)) { display('success', 200, Lang.translate('extensions_worked')); } else { display('error', 500, Lang.translate('extensions_no_plugin')); } }, function (a, e) { display('error', a.decode_code || 404, Lang.translate('title_error')); }, false, { dataType: 'text' }); } }]); return Extension; }(Item); var Recomend = /*#__PURE__*/function (_Extension) { _inherits(Recomend, _Extension); var _super = _createSuper(Recomend); function Recomend(data, params) { var _this; _classCallCheck(this, Recomend); _this = _super.call(this, data, params); _this.template = 'extensions_recomend'; return _this; } _createClass(Recomend, [{ key: "visible", value: function visible() { var _this2 = this; _get(_getPrototypeOf(Recomend.prototype), "visible", this).call(this); this.img = this.html.querySelector('.extensions__item-image'); this.img.onload = function () { _this2.img.classList.add('loaded'); }; this.img.src = Utils$1.fixMirrorLink(Utils$1.rewriteIfHTTPS(this.data.image)); } }, { key: "destroy", value: function destroy() { _get(_getPrototypeOf(Recomend.prototype), "destroy", this).call(this); if (this.img) { this.img.onload = false; this.img.onerror = false; } } }]); return Recomend; }(Extension); var Theme$1 = /*#__PURE__*/function () { function Theme() { _classCallCheck(this, Theme); } _createClass(Theme, [{ key: "init", value: function init() { if (this.get()) this.set(this.get()); } }, { key: "toggle", value: function toggle(url) { if (url) { Storage.set('cub_theme', url); this.set(url); } else { Storage.set('cub_theme', ''); $('#cub-theme').remove(); } } }, { key: "get", value: function get() { return Storage.get('cub_theme', ''); } }, { key: "set", value: function set(url) { $('#cub-theme').remove(); var href = Utils$1.rewriteIfHTTPS(Utils$1.addUrlComponent(url, 'token=' + encodeURIComponent(Storage.get('account', '{}').token))); var css = $(''); $('body').append(css); } }]); return Theme; }(); var Theme$2 = new Theme$1(); var Theme = /*#__PURE__*/function (_Item) { _inherits(Theme, _Item); var _super = _createSuper(Theme); function Theme(data, params) { var _this; _classCallCheck(this, Theme); _this = _super.call(this, data, params); _this.template = 'extensions_theme'; _this.link = Utils$1.rewriteIfHTTPS(Utils$1.protocol() + object$2.cub_domain + '/extensions/' + _this.data.id); return _this; } _createClass(Theme, [{ key: "update", value: function update() { this.html.querySelector('.extensions__item-name').innerText = this.data.name || Lang.translate('extensions_no_name'); if (this.active()) this.html.classList.add('active');else this.html.classList.remove('active'); } }, { key: "active", value: function active() { return Storage.get('cub_theme', '') == this.link; } }, { key: "visible", value: function visible() { var _this2 = this; _get(_getPrototypeOf(Theme.prototype), "visible", this).call(this); if (this.data.premium) this.premium(); this.img = this.html.querySelector('.extensions__item-image'); this.img.onload = function () { _this2.img.classList.add('loaded'); }; this.img.src = Utils$1.fixMirrorLink(Utils$1.rewriteIfHTTPS(this.data.image)); this.html.addEventListener('hover:enter', this.menu.bind(this)); } }, { key: "menu", value: function menu() { var _this3 = this; var menu = []; var controller = Controller.enabled().name; menu.push({ title: Lang.translate('extensions_' + (this.active() ? 'disable' : 'enable')), toggle: true }); Select.show({ title: Lang.translate('title_action'), items: menu, onBack: function onBack() { Controller.toggle(controller); }, onSelect: function onSelect(a) { Controller.toggle(controller); if (a.toggle) { if (!_this3.active() && _this3.data.premium && !Account$1.hasPremium()) return Lampa.Account.showCubPremium(); Theme$2.toggle(_this3.active() ? '' : _this3.link); _this3.update(); } } }); } }, { key: "destroy", value: function destroy() { _get(_getPrototypeOf(Theme.prototype), "destroy", this).call(this); if (this.img) { this.img.onload = false; this.img.onerror = false; } } }]); return Theme; }(Item); var Screensaver = /*#__PURE__*/function (_Item) { _inherits(Screensaver, _Item); var _super = _createSuper(Screensaver); function Screensaver(data, params) { var _this; _classCallCheck(this, Screensaver); _this = _super.call(this, data, params); _this.template = 'extensions_screensaver'; _this.link = Utils$1.rewriteIfHTTPS(Utils$1.protocol() + object$2.cub_domain + '/extensions/' + _this.data.id); return _this; } _createClass(Screensaver, [{ key: "update", value: function update() { this.html.querySelector('.extensions__item-name').innerText = this.data.name || Lang.translate('extensions_no_name'); this.html.querySelector('.extensions__item-time').innerText = Utils$1.parseTime(Date.now()).time; if (this.active()) this.html.classList.add('active');else this.html.classList.remove('active'); } }, { key: "active", value: function active() { return Storage.field('screensaver_type') == 'cub' && Storage.get('cub_screensaver', '') == this.link; } }, { key: "visible", value: function visible() { var _this2 = this; _get(_getPrototypeOf(Screensaver.prototype), "visible", this).call(this); if (this.data.premium) this.premium(); this.img = this.html.querySelector('.extensions__item-image'); this.img.onload = function () { _this2.img.classList.add('loaded'); }; this.img.src = Utils$1.fixMirrorLink(Utils$1.rewriteIfHTTPS(this.data.image)); this.html.addEventListener('hover:enter', this.menu.bind(this)); } }, { key: "menu", value: function menu() { var _this3 = this; var menu = []; var controller = Controller.enabled().name; menu.push({ title: Lang.translate('extensions_' + (this.active() ? 'disable' : 'enable')), toggle: true }); menu.push({ title: Lang.translate('title_watch'), watch: true }); Select.show({ title: Lang.translate('title_action'), items: menu, onBack: function onBack(a) { Controller.toggle(controller); }, onSelect: function onSelect(a) { Controller.toggle(controller); if (a.toggle) { if (_this3.active()) Storage.set('cub_screensaver', '');else { if (_this3.data.premium && !Account$1.hasPremium()) return Lampa.Account.showCubPremium(); Storage.set('cub_screensaver', _this3.link); Storage.set('screensaver_type', 'cub'); } _this3.update(); } else { Screensaver$2.show('cub', { url: _this3.link }); } } }); } }, { key: "destroy", value: function destroy() { _get(_getPrototypeOf(Screensaver.prototype), "destroy", this).call(this); if (this.img) { this.img.onload = false; this.img.onerror = false; } } }]); return Screensaver; }(Item); var Line$3 = /*#__PURE__*/function () { function Line(data, params) { _classCallCheck(this, Line); this.params = params; this.data = data; this.items = []; this.active = 0; this.view = 4; this.last; } _createClass(Line, [{ key: "create", value: function create() { var _this = this; this.scroll = new Scroll({ horizontal: true, step: window.innerWidth / 4 }); this.html = Template.js('extensions_block', {}); this.html.querySelector('.extensions__block-title').innerText = this.params.title; this.html.addEventListener('visible', this.visible.bind(this)); this.html.querySelector('.extensions__block-body').appendChild(this.scroll.render(true)); this.scroll.onWheel = function (step) { if (!Controller.own(_this)) _this.toggle(); Controller.enabled().controller[step > 0 ? 'right' : 'left'](); }; this.scroll.onScroll = this.attach.bind(this); } }, { key: "display", value: function display(num) { var _this2 = this; this.data.filter(function (p) { if (p.platform) { var platforms = p.platform.split(','); return platforms.find(function (n) { return Platform.is(n); }); } else return true; }).filter(function (e) { return e.premium ? window.lampa_settings.account_use : true; }).filter(function (e) { return !_this2.items.find(function (f) { return f.data == e; }); }).forEach(this.append.bind(this)); } }, { key: "visible", value: function visible() { this.display(this.view); Layer.visible(this.scroll.render(true)); } }, { key: "append", value: function append(elem) { var _this3 = this; var Class = Extension; if (this.params.hpu == 'recomend') Class = Recomend; if (this.params.hpu == 'theme') Class = Theme; if (this.params.hpu == 'screensaver') Class = Screensaver; var item = new Class(elem, this.params); item.create(); item.render().addEventListener('hover:focus', function () { _this3.last = item.render(); var prev_active = _this3.active; _this3.active = _this3.items.indexOf(item); if (_this3.active > 0 || prev_active > _this3.active) _this3.scroll.update(_this3.last, true); }); this.scroll.body(true).appendChild(item.render()); this.items.push(item); if (Controller.own(this)) Controller.collectionAppend(item.render()); } }, { key: "attach", value: function attach() { var size = Platform.screen('tv') ? (Math.round(this.active / this.view) + 1) * this.view + 1 : this.data.length; this.display(size); Layer.visible(this.scroll.render(true)); } }, { key: "render", value: function render() { return this.html; } }, { key: "toggle", value: function toggle() { var _this4 = this; Controller.add('extensions_line', { link: this, toggle: function toggle() { Controller.collectionSet(_this4.scroll.render(true)); Controller.collectionFocus(_this4.items.length ? _this4.last : false, _this4.scroll.render(true)); if (_this4.onToggle) _this4.onToggle(_this4); }, update: function update() {}, right: function right() { Navigator.move('right'); }, left: function left() { Navigator.move('left'); }, down: this.onDown, up: this.onUp, back: this.onBack }); Controller.toggle('extensions_line'); } }, { key: "destroy", value: function destroy() { Arrays.destroy(this.items); this.scroll.destroy(); this.html.remove(); this.items = null; } }]); return Line; }(); var Add$1 = /*#__PURE__*/function () { function Add() { var _this = this; _classCallCheck(this, Add); this.html = document.createElement('div'); this.html.classList.add('extensions__block-add'); this.html.classList.add('selector'); this.html.innerText = Lang.translate('extensions_add'); this.html.addEventListener('hover:enter', function () { if (!Storage.get('agree_installation')) { var controller = Controller.enabled().name; Modal.open({ title: Lang.translate('install_extension_rule_1'), html: Template.get('plugins_rules'), size: 'medium', buttons_position: 'outside', buttons: [{ name: Lang.translate('settings_plugins_install'), onSelect: function onSelect() { Storage.set('agree_installation', true); Modal.close(); Controller.toggle(controller); _this.input(); } }, { name: Lang.translate('cancel'), onSelect: function onSelect() { Modal.close(); Controller.toggle(controller); } }], onBack: function onBack() { Modal.close(); Controller.toggle(controller); } }); } else { _this.input(); } }); } _createClass(Add, [{ key: "input", value: function input() { var _this2 = this; Input.edit({ title: Lang.translate('extensions_set_url'), value: '', free: true, nosave: true, nomic: true }, function (new_value) { _this2.onAdd(new_value); }); } }, { key: "render", value: function render() { return this.html; } }]); return Add; }(); var Main = /*#__PURE__*/function () { function Main(params) { _classCallCheck(this, Main); this.items = []; this.active = 0; this.params = params; } _createClass(Main, [{ key: "create", value: function create() { var _this = this; this.html = Template.js('extensions'); this.scroll = new Scroll({ mask: true }); this.scroll.onWheel = function (step) { if (step > 0) _this.down();else _this.up(); }; this.scroll.append(HeadBackward(Lang.translate('settings_main_plugins'), true)); this.scroll.height(); this.html.querySelector('.extensions__body').appendChild(this.scroll.render(true)); if (this.params.store) this.loadCustomStore();else this.load(); } }, { key: "add", value: function add() { var add = new Add$1(); var line = this.items[0]; add.onAdd = function (url) { if (url) { if (url.length > 300) { line.toggle(); Noty.show(Lang.translate('account_export_fail_600')); return; } var data = { url: url, status: 1 }; var plugin = new Extension(data, { type: 'installs', autocheck: true }); plugin.create(); Plugins.add(data); $(add.render()).after(plugin.render()); line.last = add.render(); Layer.visible(line.render()); line.toggle(); } else { line.toggle(); } }; line.scroll.body(true).appendChild(add.render()); } }, { key: "loadCustomStore", value: function loadCustomStore() { var _this2 = this; this.appendLoader(); var net = new Request(); net.silent(this.params.store, function (data) { _this2.loader.remove(); net = null; if (data.results && data.results.length) { if (_this2.params.with_installed) { _this2.appendLine(Plugins.get().reverse(), { title: Lang.translate('extensions_from_memory'), type: 'installs', autocheck: true }); } data.results.forEach(function (a) { _this2.appendLine(a.results, { title: a.title || Lang.translate('player_unknown'), type: 'extensions', hpu: a.hpu, noedit: true }); }); if (_this2.params.with_installed) _this2.add(); _this2.items.slice(0, 3).forEach(function (i) { return i.display(); }); Layer.visible(_this2.html); _this2.toggle(); } else { _this2.error(); } }, function () { _this2.loader.remove(); net = null; _this2.error(); }); } }, { key: "load", value: function load() { var _this3 = this; this.appendLoader(); var need = 2; if (window.lampa_settings.plugins_store) need += 2; var status = new Status(need); status.onComplite = function () { _this3.loader.remove(); _this3.appendLine(status.data.installs, { title: Lang.translate('extensions_from_memory'), type: 'installs', autocheck: true }); if (status.data.plugins.length) _this3.appendLine(status.data.plugins, { title: Lang.translate('extensions_from_cub'), cub: true, type: 'plugins', autocheck: true }); if (status.data.best && status.data.best.length) _this3.appendLine(status.data.best, { title: Lang.translate('extensions_from_popular'), cub: true, type: 'extensions' }); if (status.data.all && status.data.all.length) _this3.appendLine(status.data.all.reverse(), { title: Lang.translate('extensions_from_lib'), cub: true, type: 'extensions' }); if (status.data.list) { status.data.list.forEach(function (data) { if (data.results.length) { _this3.appendLine(data.results, { title: Lang.translate('extensions_hpu_' + data.hpu), cub: true, type: 'extensions', hpu: data.hpu }); } }); } _this3.add(); _this3.items.slice(0, 3).forEach(function (i) { return i.display(); }); Layer.visible(_this3.html); _this3.toggle(); }; status.append('installs', Plugins.get().reverse()); Account$1.Api.plugins(function (plugins) { status.append('plugins', plugins.reverse()); }); if (window.lampa_settings.plugins_store) { CUB.extensions(function (extensions) { if (extensions.results) { status.need--; status.append('list', extensions.results); } else { status.append('best', extensions.best); status.append('all', extensions.plugins); } }); } } }, { key: "appendLoader", value: function appendLoader() { this.loader = document.createElement('div'); this.loader.classList.add('broadcast__scan'); this.loader.appendChild(document.createElement('div')); this.scroll.body(true).appendChild(this.loader); } }, { key: "error", value: function error() { var empty = new Lampa.Empty(); this.scroll.body(true).appendChild(empty.render(true)); } }, { key: "appendLine", value: function appendLine(data, params) { var _this4 = this; var line = new Line$3(data, params); line.onBack = this.onBack.bind(this); line.onUp = this.up.bind(this); line.onDown = this.down.bind(this); line.onToggle = function () { _this4.active = _this4.items.indexOf(line); _this4.scroll.update(line.render(), true); }; line.create(); this.scroll.body(true).appendChild(line.render()); this.items.push(line); } }, { key: "down", value: function down() { this.active++; if (this.active <= this.items.length - 1) this.items[this.active].toggle(); this.active = Math.min(this.items.length - 1, this.active); } }, { key: "up", value: function up() { this.active--; if (this.active >= 0) this.items[this.active].toggle(); this.active = Math.max(0, this.active); } }, { key: "render", value: function render() { return this.html; } }, { key: "toggle", value: function toggle() { var _this5 = this; Controller.add('extensions', { toggle: function toggle() { Controller.collectionSet(_this5.html); if (_this5.items.length) { _this5.items[_this5.active].toggle(); } }, back: this.onBack }); Controller.toggle('extensions'); } }, { key: "destroy", value: function destroy() { Arrays.destroy(this.items); this.scroll.destroy(); this.html.remove(); } }]); return Main; }(); var extensions; var listener$5 = Subscribe$2(); function init$A() { ParentalControl.add('extensions', { title: 'settings_main_plugins' }); } function show$4() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (extensions) return; var controller = Controller.enabled().name; extensions = new Main(params); extensions.onBack = function () { extensions.destroy(); extensions = null; document.body.toggleClass('ambience--enable', false); Controller.toggle(controller); if (params.onClose) params.onClose(); if (!params.store) listener$5.send('close', {}); }; extensions.create(); document.body.toggleClass('ambience--enable', true); document.body.appendChild(extensions.render(true)); extensions.toggle(); if (!params.store) listener$5.send('open', { extensions: extensions }); } function render$4(js) { var html = extensions ? extensions.render() : document.createElement('div'); return js ? html : $(html); } var Extensions = { init: init$A, listener: listener$5, show: show$4, render: render$4 }; var _created = []; var _loaded = []; var _network = new Request(); var _blacklist = []; var _awaits = []; var _noload = []; /** * Запуск */ function init$z() { _loaded = Storage.get('plugins', '[]'); Settings.main().render().find('[data-component="plugins"]').unbind('hover:enter').on('hover:enter', function () { ParentalControl.personal('extensions', function () { Extensions.show(); }, false, true); }); } function get$6() { return _loaded.map(function (a) { return a; }); } function modify() { var list = Storage.get('plugins', '[]'); list = list.map(function (a) { return typeof a == 'string' ? { url: a, status: 1 } : a; }); list.forEach(function (a) { a.url = (a.url + '').replace('cub.watch', object$2.cub_domain).replace('bwa.to', 'bwa.ad'); }); console.log('Plugins', 'modify:', list); Storage.set('plugins', list); } function remove$1(plug) { Arrays.remove(_loaded, plug); console.log('Plugins', 'remove:', plug, 'index:', _loaded.indexOf(plug), 'from:', _loaded); Storage.set('plugins', _loaded); } function add$6(plug) { _loaded.push(plug); console.log('Plugins', 'add:', plug); Storage.set('plugins', _loaded); push$4(plug); } function push$4(plug) { var find = _created.find(function (a) { return a == plug.url; }); if (!find && plug.status == 1) { if (_blacklist.find(function (a) { return plug.url.indexOf(a) >= 0; })) return Noty.show(Lang.translate('torrent_error_connect'), { time: 8000 }); _created.push(plug.url); console.log('Plugins', 'push:', plug); Utils$1.putScriptAsync([addPluginParams(plug.url)], false, function () { Noty.show(Lang.translate('plugins_check_fail'), { time: 8000 }); }, function () { updatePluginDB(plug.url, addPluginParams(plug.url)); Noty.show(Lang.translate('plugins_add_success')); }, false); } } function save$1() { console.log('Plugins', 'save:', _loaded); Storage.set('plugins', _loaded); } function updatePluginDB(name, url) { //if(Account.hasPremium()){ var cu = Utils$1.addUrlComponent(url, 'cache=true'); _network["native"](cu, function (str) { Cache.rewriteData('plugins', name, str).then(function () { console.log('Plugins', 'update plugin cache:', name); })["catch"](function (e) { console.warn('Plugins', 'add to cache fail:', name, typeof e == 'string' ? e : e ? e.message : 'no details'); }); }, false, false, { dataType: 'text' }); //} } function createPluginDB(name) { //if(Account.hasPremium()){ Cache.getData('plugins', name).then(function (code) { if (code) { var s = document.createElement('script'); s.type = 'text/javascript'; try { s.appendChild(document.createTextNode(code)); document.body.appendChild(s); } catch (e) { s.text = code; document.body.appendChild(s); } console.log('Plugins', 'add plugin from cache:', name); } else console.warn('Plugins', 'no find in cache:', name); })["catch"](function (e) { console.warn('Plugins', 'include from cache fail:', name, typeof e == 'string' ? e : e.message); }); //} } function addPluginParams(url) { var encode = url; encode = encode.replace('cub.watch', object$2.cub_domain); encode = Utils$1.fixMirrorLink(encode); if (!/[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}/.test(encode)) { encode = encode.replace(/\{storage_(\w+|\d+|_|-)\}/g, function (match, key) { return encodeURIComponent(Base64.encode(localStorage.getItem(key) || '')); }); if (Account$1.Permit.access) encode = Utils$1.addUrlComponent(encode, 'email=' + encodeURIComponent(Base64.encode(Account$1.Permit.account.email))); encode = Utils$1.addUrlComponent(encode, 'logged=' + encodeURIComponent(Account$1.Permit.access ? 'true' : 'false')); encode = Utils$1.addUrlComponent(encode, 'reset=' + Math.random()); encode = Utils$1.addUrlComponent(encode, 'origin=' + encodeURIComponent(Base64.encode(window.location.host))); encode = Utils$1.rewriteIfHTTPS(encode); } return encode; } function loadBlackList(call) { if (window.lampa_settings.disable_features.blacklist) return call([]); var status = new Status(2); status.onComplite = function (res) { call([].concat(res.cub, res.custom)); }; _network.silent(Utils$1.protocol() + object$2.cub_domain + '/api/plugins/blacklist', function (result) { var list = result.map(function (a) { return a.url; }); Storage.set('plugins_blacklist', list); status.append('cub', list); }, function () { status.append('cub', Storage.get('plugins_blacklist', '[]')); }, false, { timeout: 1000 * 5 }); _network.silent('./plugins_black_list.json', function (list) { status.append('custom', list); }, function () { status.append('custom', []); }, false, { timeout: 1000 * 5 }); } /** * Загрузка всех плагинов */ function load(call) { var errors = []; var original = {}; var include = []; _awaits.forEach(function (url) { var encode = addPluginParams(url); include.push(encode); original[encode] = url; }); Utils$1.putScriptAsync(include, function () { call(); if (errors.length) { setTimeout(function () { Noty.show(Lang.translate('plugins_no_loaded') + ' (' + errors.join(', ') + ')', { time: 6000 }); }, 2000); } }, function (u) { if (u.indexOf('modification.js') == -1) { console.warn('Extensions', 'error:', original[u]); errors.push(original[u]); _noload.push(original[u]); createPluginDB(original[u]); } }, function (u) { console.log('Extensions', 'include:', original[u]); _created.push(original[u]); updatePluginDB(original[u], u); }, false); } function task$1(call) { modify(); _loaded = Storage.get('plugins', '[]'); loadBlackList(function (black_list) { Account$1.Api.plugins(function (plugins) { var puts = window.lampa_settings.plugins_use ? plugins.filter(function (plugin) { return plugin.status; }).map(function (plugin) { return plugin.url; }).concat(Storage.get('plugins', '[]').filter(function (plugin) { return plugin.status; }).map(function (plugin) { return plugin.url; })) : []; puts.push('./plugins/modification.js'); puts = puts.filter(function (element, index) { return puts.indexOf(element) === index; }); console.log('Plugins', 'load list:', puts); black_list.push('lipp.xyz'); black_list.push('llpp.xyz'); black_list.push('scabrum.github.io'); black_list.push('bylampa.github.io'); black_list.push('tinyurl.com'); // Stupid people :( black_list.push('t.me/'); black_list.push('4pda.'); black_list.push('teletype.in'); black_list.push('yotube.com'); _blacklist = black_list; console.log('Plugins', 'black list:', black_list); black_list.forEach(function (b) { puts = puts.filter(function (p) { return p.toLowerCase().indexOf(b) == -1; }); }); console.log('Plugins', 'clear list:', puts); _awaits = puts; call(); }); }); } function awaits() { return _awaits; } var Plugins = { init: init$z, load: load, remove: remove$1, loaded: function loaded() { return _created; }, errors: function errors() { return _noload; }, add: add$6, get: get$6, save: save$1, push: push$4, task: task$1, awaits: awaits }; /** * Показать шаблон пустого экрана * @doc * @name class * @alias Empty * @param {Object} params заголовок и описание JSON({"title":"Заголовок","descr":"Описание"}) * @returns {Object} объект класса */ var Empty = /*#__PURE__*/function (_Emit) { _inherits(Empty, _Emit); var _super = _createSuper(Empty); function Empty() { var _this; var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, Empty); _this = _super.call(this); Arrays.extend(params, { title: Lang.translate('empty_title_two'), descr: Lang.translate('empty_text_two'), text: Lang.translate('empty_text_two'), noicon: false, width: 'large', template: 'empty', icon: '', buttons: [] }); _this.params = params; _this.html = Template.get(params.template, params); _this.html.addClass('layer--wheight'); if (params.noicon) _this.noicon();else if (params.icon) _this.html.addClass('empty--custom-icon').find('.empty__icon').append(params.icon); params.buttons.push({ title: Lang.translate('terminal_update'), onEnter: function onEnter() { delete params.buttons; Activity.replace(); } }); if (params.buttons.length) _this.addButtons(params.buttons); _this.width(params.width); _this.emit('init'); return _this; } _createClass(Empty, [{ key: "noicon", value: function noicon() { this.html.addClass('empty--noicon'); } }, { key: "width", value: function width(_width) { this.html.removeClass('empty--width-large empty--width-medium empty--width-small'); this.html.addClass('empty--width-' + _width); } }, { key: "start", value: function start() { var _this2 = this; this.emit('start'); var controller = { link: this, toggle: function toggle() { var selects = _this2.html.find('.selector').filter(function () { return !$(this).hasClass('empty__img'); }); _this2.html.find('.empty__img').toggleClass('selector', selects.length > 0 ? false : true); Controller.collectionSet(_this2.html); Controller.collectionFocus(selects.length > 0 ? selects.eq(0)[0] : false, _this2.html); Background.change(TMDB.img('/oXPYD4c3bLtfAS2FzwjZh7NWqo4.jpg', 'w200')); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else Controller.toggle('menu'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('head'); }, down: function down() { Navigator.move('down'); }, right: function right() { Navigator.move('right'); }, back: function back() { Activity.backward(); } }; this.emit('controller', controller); Controller.add('content', controller); Controller.toggle('content'); } }, { key: "addButtons", value: function addButtons(buttons) { var footer = this.html.find('.empty__footer'); if (!footer.length) { footer = $(''); this.html.append(footer); } buttons.forEach(function (button_data) { var button = $("
    ".concat(button_data.title, "
    ")); button.on('hover:enter', function () { if (button_data.onEnter) button_data.onEnter(); }); footer.append(button); }); } }, { key: "addInfoButton", value: function addInfoButton(add_information) { var footer = this.html.find('.empty__footer'); if (!footer.length) { footer = $(''); this.html.append(footer); } var button = $('
    ' + Lang.translate('extensions_info') + '
    '); button.on('hover:enter', function () { var controller = Controller.enabled().name; var html = $('
    '); var line = function line(name, value) { html.append($("
    ".concat(name, " - ").concat(value, "
    "))); }; line('Protocol', window.location.protocol); line('Host', window.location.host); line('Platform', Platform.get()); line('Safe connection', Storage.field('protocol') == 'https' ? 'Yes' : 'No'); line('Connection', Utils$1.protocol()); line('TMDB Proxy', Storage.field('tmdb_proxy') ? 'Yes' : 'No'); line('TMDB Proxy plugin', Plugins.loaded().find(function (u) { return /\/plugin\/tmdb-proxy/.test(u); }) ? 'Yes' : 'No'); line('TMDB Proxy api', Lampa.TMDB.api('').split('/').slice(0, 3).join('/')); line('Premium', Account$1.hasPremium() ? 'Yes' : 'No'); line('Mirror', object$2.cub_domain); if (add_information) { add_information.forEach(function (info) { line(info[0], info[1]); }); } Modal.open({ title: Lang.translate('extensions_info'), html: html, size: 'medium', onBack: function onBack() { Modal.close(); Controller.toggle(controller); } }); }); footer.append(button); } }, { key: "append", value: function append(add) { this.html.append(add); } }, { key: "render", value: function render(js) { return js ? this.html[0] : this.html; } }, { key: "destroy", value: function destroy() { this.html.remove(); this.emit('destroy'); } }]); return Empty; }(Emit); var Simple = { onEmpty: function onEmpty() { var params = this.params.empty || {}; params.buttons = params.buttons || []; if (this.object.source == 'tmdb' && params.cub_button) { params.buttons.push({ title: Lang.translate('change_source_on_cub'), onEnter: function onEnter() { Storage.set('source', 'cub'); Activity.replace({ source: 'cub' }); } }); } this.empty_class = new Empty(params); if (params.info_button) this.empty_class.addInfoButton(params.info_button); this.scroll.append(this.empty_class.render(true)); this.start = this.empty_class.start.bind(this.empty_class); } }; var MainMap = { Items: Items$1, Create: Create$1, Next: Next$1, Empty: Simple, Callback: Callback }; var Helper$7 = new Mask(Arrays.getKeys(MainMap)); Helper$7.MASK.base = Helper$7.MASK.all; var Full$1 = /*#__PURE__*/function (_Base) { _inherits(Full, _Base); var _super = _createSuper(Full); function Full(object) { var _this; _classCallCheck(this, Full); _this = _super.call(this, object); var module = typeof _this.params.module !== 'undefined' ? _this.params.module : Helper$7.MASK.base; Helper$7.getNames(module).map(function (name) { return MainMap[name]; }).forEach(function (mod) { return _this.use(mod); }); _this.emit('init'); return _this; } return Full; }(Main$1); /** * Компонент главной страницы * @param {object} object - Параметры компонента * @return {object} - Экземпляр компонента */ function component$n(object) { var comp = Utils$1.createInstance(Full$1, object); var next = null; comp.use({ onCreate: function onCreate() { var nextCall = Api.main(object, this.build.bind(this), this.empty.bind(this)); if (typeof nextCall == 'function') next = nextCall; }, onNext: function onNext(resolve, reject) { if (next) { next(resolve.bind(this), reject.bind(this)); } else reject.call(this); }, onInstance: function onInstance(item, data) { item.use({ onMore: router.call.bind(router, 'category_full', data), onInstance: function onInstance(card, data) { card.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); } }); return comp; } var Options = { onCreate: function onCreate() { var _this = this; this.html.find('.button--options').on('hover:enter', function () { var options = []; Lampa.Listener.send('full', { link: _this, type: 'options', props: _this.props, options: options }); if (!window.lampa_settings.disable_features.ai) { if (options.length) { options.push({ title: Lang.translate('title_ai_assistant'), separator: true }); } options.push({ title: Lang.translate('title_recomendations'), onSelect: function onSelect() { Controller.toggle('content'); Activity.push({ url: '', title: Lang.translate('title_recomendations'), component: 'ai_recommendations', card: _this.card }); } }); options.push({ title: Lang.translate('title_facts'), onSelect: function onSelect() { Controller.toggle('content'); Activity.push({ url: '', title: Lang.translate('title_facts'), component: 'ai_facts', card: _this.card }); } }); } Select.show({ title: Lang.translate('more'), items: options, onSelect: function onSelect() { Controller.toggle('content'); }, onBack: function onBack() { Controller.toggle('content'); } }); }); } }; var Torrents = { onCreate: function onCreate() { var _this = this; var status = Storage.field('parser_use'); var button = this.html.find('.view--torrent'); if (window.lampa_settings.torrents_use) button.toggleClass('selector', status).toggleClass('hide', !status); button.on('hover:enter', function () { var year = ((_this.card.first_air_date || _this.card.release_date || '0000') + '').slice(0, 4); var combinations = { 'df': _this.card.original_title, 'df_year': _this.card.original_title + ' ' + year, 'df_lg': _this.card.original_title + ' ' + _this.card.title, 'df_lg_year': _this.card.original_title + ' ' + _this.card.title + ' ' + year, 'lg': _this.card.title, 'lg_year': _this.card.title + ' ' + year, 'lg_df': _this.card.title + ' ' + _this.card.original_title, 'lg_df_year': _this.card.title + ' ' + _this.card.original_title + ' ' + year }; Activity.push({ url: '', title: Lang.translate('title_torrents'), component: 'torrents', search: combinations[Storage.field('parse_lang')], search_one: _this.card.title, search_two: _this.card.original_title, movie: _this.card, page: 1 }); }); } }; var Subscribed = { onCreate: function onCreate() { if (!(this.card.source == 'tmdb' || this.card.source == 'cub')) this.html.find('.source--name').text(this.card.source.toUpperCase());else if (this.card.number_of_seasons && window.lampa_settings.account_use && !window.lampa_settings.disable_features.subscribe) { this.html.find('.button--subscribe').removeClass('hide'); this.emit('subscribed'); } }, onSubscribed: function onSubscribed() { var _this = this; Api$1.load('card/subscribed', {}, { id: this.card.id, imdb_id: this.card.imdb_id }).then(function (result) { _this.html.find('.button--subscribe').data('voice', result.translation.voice).addClass('active').find('path').attr('fill', 'currentColor'); })["catch"](function (e) {}); } }; var Trailers = { onCreate: function onCreate() { var _this = this; var videos = this.data.videos; if (!this.data.movie.adult && videos && videos.results.length && !window.lampa_settings.disable_features.trailers) { this.html.find('.view--trailer').on('hover:enter', function () { var items = []; videos.results.forEach(function (element) { var date = Utils$1.parseTime(element.published_at).full; items.push({ title: Utils$1.shortText(element.name, 50), subtitle: (element.official ? Lang.translate('full_trailer_official') : Lang.translate('full_trailer_no_official')) + ' - ' + date, id: element.key, player: element.player, code: element.iso_639_1, time: new Date(element.published_at).getTime(), url: element.url || 'https://www.youtube.com/watch?v=' + element.key, youtube: typeof element.youtube !== 'undefined' ? element.youtube : true, icon: '', template: 'selectbox_icon', thumbnail: element.icon || 'https://img.youtube.com/vi/' + element.key + '/default.jpg' }); }); items.sort(function (a, b) { return a.time > b.time ? -1 : a.time < b.time ? 1 : 0; }); var my_lang = items.filter(function (n) { return n.code == Storage.field('tmdb_lang'); }); var en_lang = items.filter(function (n) { return n.code == 'en' && my_lang.indexOf(n) == -1; }); var al_lang = []; if (my_lang.length) { al_lang = al_lang.concat(my_lang); } if (al_lang.length && en_lang.length) { al_lang.push({ title: Lang.translate('more'), separator: true }); } al_lang = al_lang.concat(en_lang); Select.show({ title: 'YouTube - ' + Lang.translate('title_trailers'), items: al_lang, onSelect: function onSelect(a) { _this.toggle(); if (Platform.is('android') && Storage.field('player_launch_trailers') == 'youtube' && a.youtube) { Android.openYoutube(a.id); } // else if(Platform.is('webos') && window.location.protocol == 'file:'){ // if (window.webOS && webOS.service) { // webOS.service.request( // 'luna://com.webos.applicationManager', // { // method: 'launch', // parameters: { // id: 'youtube.leanback.v4', // params: { // contentTarget: 'v=' + a.id // } // }, // onSuccess: function (inResponse) { // console.log('YouTube','WebOS launched YouTube app successfully') // }, // onFailure: function (inError) { // if(parseInt(inError.errorCode) == 100){ // Lampa.Noty.show(Lang.translate('player_youtube_no_support')) // } // else{ // Lampa.Noty.show(Lang.translate('network_error') + ' ' + "[" + inError.errorCode + "] " + inError.errorText) // } // } // } // ) // } // else{ // Lampa.Noty.show(Lang.translate('network_error') + ' ' + "[WebOS service not available]") // } // } else { var playlist = al_lang.filter(function (v) { return !v.separator; }); Player.play(a); Player.playlist(playlist); } }, onBack: function onBack() { Controller.toggle('content'); } }); }); } else { this.html.find('.view--trailer').remove(); } } }; var Poster = { onCreate: function onCreate() { var _this = this; var im = this.html.find('.full--poster'); var poster; this.img_poster = im[0] || {}; if (window.innerWidth <= 480) { this.img_poster = new Image(); this.img_poster.crossOrigin = "Anonymous"; } this.img_poster.onerror = function (e) { _this.img_poster.src = './img/img_broken.svg'; }; this.img_poster.onload = function (e) { im.parent().addClass('loaded'); }; if (window.innerWidth <= 480) { if (this.card.backdrop_path) poster = Api.img(this.card.backdrop_path, 'w780');else if (this.card.background_image) poster = this.card.background_image; this.img_poster.onerror = function (e) { _this.img_poster = im[0]; _this.img_poster.onerror = function (e) { _this.img_poster.src = './img/img_broken.svg'; }; _this.img_poster.onload = function (e) { im.parent().addClass('loaded').addClass('with-out'); }; im[0].src = poster || _this.card.img; }; this.img_poster.onload = function (e) { var draw = function draw() { im.parent().addClass('loaded'); setTimeout(function () { im[0].style.transition = 'none'; }, 500); var sc = _this.html.parents('.scroll')[0]; var an = 0; var ts = 0; var dl = window.innerHeight * 0.1; var smoothParallax = function smoothParallax() { im[0].style.transform = 'translate3d(0, ' + ts * 0.35 + 'px, 0)'; im[0].style.opacity = ts >= dl ? Math.max(0, 1 - (ts - dl) / (window.innerHeight * 0.2)) : 1; an--; if (an > 0) smoothParallax(); }; if (sc) sc.on('scroll', function (e) { ts = sc.scrollTop; if (an == 0) { an = 100; requestAnimationFrame(smoothParallax); } }); }; if (window.lampa_settings.blur_poster) { Color.blurPoster(_this.img_poster, im.width(), im.height(), function (nim) { im[0].src = nim.src; draw(); }); } else { im[0].src = _this.img_poster.src; draw(); } }; } if (poster) this.html.find('.full-start__poster').addClass('background--poster'); this.img_poster.src = poster || this.card.img; } }; var Reactions = { onCreate: function onCreate() { var _this = this; if (!Storage.field('card_interfice_reactions') || window.lampa_settings.disable_features.reactions) return this.html.find('.full-start-new__reactions, .button--reaction').remove(); var reactions_data = this.data.reactions; var vote = function vote(type) { var add = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var mine = Storage.get('mine_reactions', {}); var id = (_this.card.name ? 'tv' : 'movie') + '_' + _this.card.id; if (!mine[id]) mine[id] = []; var ready = mine[id].indexOf(type) >= 0; if (add) { if (!ready) mine[id].push(type); Storage.set('mine_reactions', mine); } return ready; }; var draw = function draw() { if (reactions_data && reactions_data.result && reactions_data.result.length) { var reactions = reactions_data.result; var reactions_body = _this.html.find('.full-start-new__reactions')[0]; reactions.sort(function (a, b) { return a.counter > b.counter ? -1 : a.counter < b.counter ? 1 : 0; }); reactions_body.empty(); reactions.forEach(function (r) { var reaction = document.createElement('div'), icon = document.createElement('img'), count = document.createElement('div'), wrap = document.createElement('div'); reaction.addClass('reaction'); icon.addClass('reaction__icon'); count.addClass('reaction__count'); reaction.addClass('reaction--' + r.type); count.text(Utils$1.bigNumberToShort(r.counter)); icon.style.opacity = 0; Utils$1.imgLoad(icon, Utils$1.protocol() + object$2.cub_domain + '/img/reactions/' + r.type + '.svg', function () { icon.style.opacity = 1; }); reaction.append(icon); reaction.append(count); wrap.append(reaction); if (vote(r.type)) reaction.addClass('reaction--voted'); reactions_body.append(wrap); }); } }; var items = [{ type: 'fire' }, { type: 'nice' }, { type: 'think' }, { type: 'bore' }, { type: 'shit' }]; items.forEach(function (a) { a.template = 'selectbox_icon', a.icon = ''; a.ghost = vote(a.type); a.noenter = a.ghost; a.title = Lang.translate('reactions_' + a.type); }); this.html.find('.button--reaction').on('hover:enter', function () { Select.show({ title: Lang.translate('title_reactions'), items: items, onSelect: function onSelect(a) { Controller.toggle('content'); Api.sources.cub.reactionsAdd({ method: _this.card.name ? 'tv' : 'movie', id: _this.card.id, type: a.type }, function () { vote(a.type, true); var find = reactions_data.result.find(function (r) { return r.type == a.type; }); if (find) find.counter++;else { reactions_data.result.push({ type: a.type, counter: 1 }); } a.ghost = true; a.noenter = true; draw(); }, function (e) { Lampa.Noty.show(Lang.translate('reactions_ready')); }); }, onBack: function onBack() { Controller.toggle('content'); } }); }); draw(); } }; var Translations = { onCreate: function onCreate() { var _this = this; if (window.lampa_settings.disable_features.subscribe) return; var button = this.html.find('.button--subscribe'); button.on('hover:enter', function () { if (button.hasClass('loading')) return; if (!Account$1.Permit.access) { return Account$1.Modal.account(); } button.addClass('loading'); Api$1.load('card/translations', {}, { id: _this.card.id, imdb_id: _this.card.imdb_id, season: Utils$1.countSeasons(_this.card) }).then(function (data) { var items = []; var subscribed = data.translations.subscribed || button.data('voice'); if (subscribed) { items.push({ title: Lang.translate('title_unsubscribe'), subtitle: subscribed, unsubscribe: true }); data.translations.voices = data.translations.voices.filter(function (voice) { return voice !== subscribed; }); } data.translations.voices.forEach(function (voice) { items.push({ title: voice, voice: voice, ghost: true, episode: 1 }); }); if (items.length) { Select.show({ title: Lang.translate('title_subscribe'), items: items, onSelect: function onSelect(a) { _this.toggle(); if (a.unsubscribe) { Api$1.load('card/unsubscribe', {}, { id: _this.card.id }).then(function () { button.removeClass('active').data('voice', '').find('path').eq(1).attr('fill', 'transparent'); })["catch"](function (e) { Noty.show(Lang.translate('subscribe_error')); }); } else { Account$1.Api.subscribeToTranslation({ card: _this.card, season: Utils$1.countSeasons(_this.card), episode: a.episode, voice: a.voice }, function () { Bell.push({ text: Lang.translate('subscribe_success') }); button.addClass('active').data('voice', a.voice).find('path').attr('fill', 'currentColor'); }, function () { Noty.show(Lang.translate('subscribe_error')); }); } }, onFullDraw: function onFullDraw(scroll) { scroll.body(true).prepend(Template.elem('div', { "class": 'selectbox__text', children: [Template.elem('div', { text: Lang.translate('subscribe_info') })] })); }, onBack: function onBack() { Controller.toggle('content'); } }); } else Noty.show(Lang.translate('subscribe_noinfo')); })["catch"](function (e) {})["finally"](function () { button.removeClass('loading'); }); }); } }; var Bookmarks = { onCreate: function onCreate() { var _this = this; var btn = this.html.find('.button--book'); btn.on('hover:enter', function () { if (btn.hasClass('loading')) return; var status = Favorite.check(_this.card); var marks = ['look', 'viewed', 'scheduled', 'continued', 'thrown']; var label = function label(a) { btn.addClass('loading'); Favorite.toggle(a.type, _this.card); if (a.collect) Controller.toggle('content'); }; var items = ['book', 'like', 'wath', 'history'].map(function (type) { return { title: Lang.translate('title_' + type), type: type, checkbox: true, checked: status[type] }; }); if (window.lampa_settings.account_use) { items.push({ title: Lang.translate('settings_cub_status'), separator: true }); marks.forEach(function (m) { items.push({ title: Lang.translate('title_' + m), type: m, picked: status[m], collect: true, noenter: !Account$1.hasPremium(), ghost: !Account$1.hasPremium() }); }); } Select.show({ title: Lang.translate('settings_input_links'), items: items, onCheck: label, onSelect: label, onBack: function onBack() { btn.removeClass('loading'); Controller.toggle('content'); }, onDraw: function onDraw(item, elem) { if (elem.collect) { if (!Account$1.hasPremium()) { item.on('hover:enter', function () { Select.close(); Account$1.Modal[Account$1.Permit.token ? 'premium' : 'account'](); }); } } } }); }); this.emit('updateFavorite'); this.listenerFavorite = function (e) { if (e.target == 'favorite') { btn.removeClass('loading'); if (e.card) { if (e.card.id == _this.card.id) _this.emit('updateFavorite'); } else _this.emit('updateFavorite'); } }; Lampa.Listener.follow('state:changed', this.listenerFavorite); }, onUpdateFavorite: function onUpdateFavorite() { var status = Favorite.check(this.card); var any = Favorite.checkAnyNotHistory(status); var marks = ['look', 'viewed', 'scheduled', 'continued', 'thrown']; var any_marker = marks.find(function (m) { return status[m]; }); $('.button--book path', this.html).attr('fill', any ? 'currentColor' : 'transparent'); this.html.find('.full-start-new__poster .card__marker').remove(); if (any_marker && Platform.screen('tv')) { var marker = Template.elem('div', { "class": 'card__marker', children: [Template.elem('span')] }); this.html.find('.full-start-new__poster').append(marker); marker.find('span').text(Lang.translate('title_' + any_marker)); marker.removeClass(marks.map(function (m) { return 'card__marker--' + m; }).join(' ')).addClass('card__marker--' + any_marker); } }, onDestroy: function onDestroy() { Lampa.Listener.remove('state:changed', this.listenerFavorite); } }; var Buttons = { onPriorityButton: function onPriorityButton(btn) { var _this = this; var cont = this.html.find('.full-start-new__buttons'); var clon = btn.clone(); cont.find('.button--priority').remove(); clon.addClass('button--priority').removeClass('view--torrent').on('hover:enter', function () { btn.trigger('hover:enter'); }).on('hover:long', function () { clon.remove(); Storage.set('full_btn_priority', ''); _this.last = _this.html.find('.button--play')[0]; Controller.toggle('content'); }); cont.prepend(clon); }, onGroupButtons: function onGroupButtons() { var _this2 = this; var play = this.html.find('.button--play'); var btns = this.html.find('.buttons--container > .full-start__button').not('.hide'); var priority = Storage.get('full_btn_priority', '') + ''; if (priority) { var priority_button; btns.each(function () { var hash = Utils$1.hash($(this).clone().removeClass('focus').prop('outerHTML')); if (hash == priority) priority_button = $(this); }); if (priority_button) this.emit('priorityButton', priority_button); } play.unbind().on('hover:enter', function (e) { priority = Storage.get('full_btn_priority', '') + ''; btns = _this2.html.find('.buttons--container > .full-start__button').not('.hide').filter(function () { return priority !== Utils$1.hash($(this).clone().removeClass('focus').prop('outerHTML')); }); if (btns.length == 1) { btns.trigger('hover:enter'); } else { var items = []; btns.each(function () { var icon = $(this).find('svg').prop('outerHTML'); items.push({ title: $(this).text(), subtitle: $(this).data('subtitle'), template: typeof icon == 'undefined' || icon == 'undefined' ? 'selectbox_item' : 'selectbox_icon', icon: icon, btn: $(this) }); }); Select.show({ title: Lang.translate('settings_rest_source'), items: items, onSelect: function onSelect(a) { a.btn.trigger('hover:enter'); }, onLong: function onLong(a) { Storage.set('full_btn_priority', Utils$1.hash(a.btn.clone().removeClass('focus').prop('outerHTML'))); _this2.emit('priorityButton', a.btn); }, onBack: function onBack() { Controller.toggle('content'); } }); } }).on('hover:focus', function () { _this2.last = play[0]; }); play.toggleClass('hide', !Boolean(btns.length)); } }; var Start = /*#__PURE__*/function (_Emit) { _inherits(Start, _Emit); var _super = _createSuper(Start); function Start(data) { var _this; _classCallCheck(this, Start); _this = _super.call(this); _this.data = data; _this.card = data.movie; Arrays.extend(_this.card, { title: _this.card.name, original_title: _this.card.original_name, runtime: 0, img: _this.card.poster_path ? Api.img(_this.card.poster_path, Storage.field('poster_size')).replace(/\/w\d{1,4}/, '/w500') : './img/img_broken.svg' }); Utils$1.canWatchChildren(TMDB.parsePG(data.movie), permit.profile.age) && _this.use(Torrents); _this.use(Options); _this.use(Subscribed); _this.use(Trailers); _this.use(Poster); _this.use(Translations); _this.use(Bookmarks); _this.use(Reactions); _this.use(Buttons); _this.emit('init'); return _this; } _createClass(Start, [{ key: "create", value: function create() { var _this2 = this; var genres = (this.card.genres || ['---']).slice(0, 3).map(function (a) { return Utils$1.capitalizeFirstLetter(a.name); }).join(', '); var countries = TMDB.parseCountries(this.card); var seasons = Utils$1.countSeasons(this.card); var tmdb_rating = parseFloat((this.card.vote_average || 0) + '').toFixed(1); var relise = (this.card.release_date || this.card.first_air_date || '') + ''; var year = relise ? relise.slice(0, 4) : ''; var quality = !this.card.first_air_date ? this.card.release_quality || this.card.quality : false; var pg = TMDB.parsePG(this.card); var head = []; var info = []; if (tmdb_rating >= 10) tmdb_rating = 10; this.html = Template.get('full_start_new', { title: this.card.title, original_title: this.card.original_title, descr: Utils$1.substr(this.card.overview || Lang.translate('full_notext'), 420), time: Utils$1.secondsToTime(this.card.runtime * 60, true), genres: Utils$1.substr(genres, 30), rating: tmdb_rating, seasons: seasons, countries: countries.join(', '), episodes: this.card.number_of_episodes, tagline: this.card.tagline }); !window.lampa_settings.torrents_use && this.html.find('.view--torrent').addClass('hide'); this.card.name && this.html.find('.full-start-new__poster').addClass('card--tv').append('
    TV
    '); this.card.adult && this.html.find('.full-start-new__poster').addClass('card--adult').append('
    ADULT
    '); if (year) { this.html.find('.tag--year').removeClass('hide').find('> div').text(year); head.push('' + year + ''); } countries.length && head.push(countries.slice(0, 5).join(' | ')); !this.card.tagline && this.html.find('.full--tagline').remove(); if (!this.card.tagline && this.card.title.length > 25) this.html.find('.full-start-new__title').addClass('twolines'); this.card.runtime > 0 && info.push('' + Utils$1.secondsToTime(this.card.runtime * 60, true) + ''); seasons && info.push('' + Lang.translate('title_seasons') + ': ' + seasons + ''); pg && this.html.find('.full-start__pg').removeClass('hide').text(pg); this.card.number_of_episodes && info.push('' + Lang.translate('title_episodes') + ': ' + this.card.number_of_episodes + ''); !this.card.runtime && $('.tag--time', this.html).remove(); this.card.number_of_seasons && this.html.find('.is--serial').removeClass('hide'); this.card.vote_average == 0 && this.html.find('.rate--tmdb').addClass('hide'); this.card.status && this.html.find('.full-start__status').removeClass('hide').text(Lang.translate('tv_status_' + this.card.status.toLowerCase().replace(/ /g, '_'))); if (this.card.imdb_rating && parseFloat(this.card.imdb_rating) > 0) { this.html.find('.rate--imdb').removeClass('hide').find('> div').eq(0).text(parseFloat(this.card.imdb_rating) >= 10 ? 10 : this.card.imdb_rating); } if (this.card.kp_rating && parseFloat(this.card.kp_rating) > 0) { this.html.find('.rate--kp').removeClass('hide').find('> div').eq(0).text(parseFloat(this.card.kp_rating) >= 10 ? 10 : this.card.kp_rating); } if (this.card.genres) { genres = this.card.genres.slice(0, 5).map(function (a) { return Utils$1.capitalizeFirstLetter(a.name); }).join(' | '); info.push('' + genres + ''); } if (quality) { this.html.find('.tag--quality').removeClass('hide').find('> div').text(quality); info.push('' + Lang.translate('player_quality') + ': ' + quality.toUpperCase() + ''); } if (this.card.next_episode_to_air) { var air = Utils$1.parseToDate(this.card.next_episode_to_air.air_date); var now = Date.now(); var day = Math.ceil((air.getTime() - now) / (24 * 60 * 60 * 1000)); var txt = Lang.translate('full_next_episode') + ': ' + Utils$1.parseTime(this.card.next_episode_to_air.air_date)["short"] + ' / ' + Lang.translate('full_episode_days_left') + ': ' + day; if (day > 0) { $('.tag--episode', this.html).removeClass('hide').find('div').text(txt); info.push('' + txt + ''); } } $('.full-start-new__buttons', this.html).find('.selector').on('hover:focus hover:enter hover:hover hover:touch', function (e) { _this2.last = e.target; }); this.html.find('.full-start-new__head').toggleClass('hide', !head.length).html(head.join(', ')); this.html.find('.full-start-new__details').html(info.join('●')); if (window.lampa_settings.read_only) this.html.find('.button--play').remove(); this.emit('create'); } }, { key: "toggle", value: function toggle() { var _this3 = this; var controller = { link: this, toggle: function toggle() { _this3.emit('groupButtons'); Controller.collectionSet(_this3.html); Controller.collectionFocus(_this3.last || false, _this3.html); _this3.emit('toggle'); }, right: function right() { Navigator.move('right'); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else _this3.emit('left'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down');else _this3.emit('down'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else _this3.emit('up'); }, back: this.emit.bind(this, 'back') }; this.emit('controller', controller); Controller.add('full_start', controller); Controller.toggle('full_start'); } }, { key: "render", value: function render(js) { return js ? this.html[0] : this.html; } }, { key: "destroy", value: function destroy() { this.html.remove(); this.emit('destroy'); } }]); return Start; }(Emit); function Warning(params) { this.params = params; this.html = Template.get('warning', params); if (params.button) { var button = $('
    ' + (params.button.title || '') + '
    '); this.html.find('.warning-box__right').toggleClass('hide', false).append(button); } if (params.onSelect) { this.html.addClass('selector').on('hover:enter', params.onSelect); } this.render = function (js) { return js ? this.html[0] : this.html; }; this.destroy = function () { this.html.remove(); this.html = null; }; } var Descriptiopn = /*#__PURE__*/function (_Emit) { _inherits(Descriptiopn, _Emit); var _super = _createSuper(Descriptiopn); function Descriptiopn(data) { var _this; _classCallCheck(this, Descriptiopn); _this = _super.call(this); _this.data = data; _this.card = data.movie; _this.emit('init'); return _this; } _createClass(Descriptiopn, [{ key: "create", value: function create() { var _this2 = this; this.html = Template.get('items_line', { title: Lang.translate('full_detail') }); var media = this.card.name ? 'tv' : 'movie'; var countries = TMDB.parseCountries(this.card); var date = (this.card.release_date || this.card.first_air_date || '') + ''; this.body = Template.get('full_descr', { text: (this.card.overview || Lang.translate('full_notext')) + '

    ', relise: date.length > 3 ? Utils$1.parseTime(date).full : date.length > 0 ? date : Lang.translate('player_unknown'), budget: '$ ' + Utils$1.numberWithSpaces(this.card.budget || 0), countries: countries.join(', ') }); var tags = this.body.find('.full-descr__tags'); if (this.card.genres.length) { tags.append(this.tag(Lang.translate('full_genre'), this.card.genres, function (genre) { Activity.push({ url: genre.url || media, title: Utils$1.capitalizeFirstLetter(genre.name), component: _this2.card.source == 'cub' ? 'category' : 'category_full', genres: genre.id, source: _this2.card.source, page: 1 }); })); } if (this.card.production_companies.length) { tags.append(this.tag(Lang.translate('full_production'), this.card.production_companies, function (company) { router.call('company', _objectSpread2(_objectSpread2({}, company), {}, { card: _this2.card })); })); } var key_tags = this.card.keywords ? this.card.keywords.results || this.card.keywords.keywords : []; if (key_tags.length && key_tags.find) { var tags_filter = key_tags.filter(function (key) { return !Keys.adult.find(function (tag) { return tag.indexOf(key.name.toLowerCase()) >= 0; }); }); tags_filter = tags_filter.filter(function (key) { return !Keys.lgbt.find(function (tag) { return tag.indexOf(key.name.toLowerCase()) >= 0; }); }); if (tags_filter.length) { tags.append(this.tag(Lang.translate('full_keywords'), tags_filter, function (key) { Activity.push({ url: 'discover/' + media, title: Utils$1.capitalizeFirstLetter(key.name), keywords: key.id, component: 'category_full', source: 'tmdb', page: 1 }); })); } } if (!this.card.budget) $('.full--budget', this.body).remove(); if (!countries.length) $('.full--countries', this.body).remove(); this.body.find('.selector').on('hover:focus hover:enter hover:hover hover:touch', function (e) { _this2.last = e.target; }); if (this.card.adult) { var warning = new Warning({ type: 'full-adult', title: Lang.translate('adult_content_title'), text: Lang.translate('adult_content_text_warning'), icon: '', button: { title: Lang.translate('title_watch') }, onSelect: function onSelect() { Modal.open({ title: Lang.translate('adult_content_title'), size: 'small', scroll: { nopadding: true }, html: $('
    ' + Lang.translate('adult_content_text_modal') + '
    '), buttons: [{ name: Lang.translate('adult_content_confirm'), onSelect: function onSelect() { Modal.close(); Controller.toggle('full_descr'); Storage.set('adult_content_view', true); Lampa.Activity.refresh(); } }, { name: Lang.translate('adult_content_deny'), onSelect: function onSelect() { Modal.close(); Controller.toggle('full_descr'); } }], onBack: function onBack() { Modal.close(); Controller.toggle('full_descr'); } }); } }); $('.full-descr__left', this.body).append(warning.render()); } this.html.find('.items-line__body').append(this.body); this.emit('create'); } }, { key: "tag", value: function tag(name, items, call) { var _this3 = this; var elem = $("
    \n
    ".concat(name, "
    \n
    ").concat(items.length, "
    \n
    ")); elem.on('hover:enter', function () { var select = items.map(function (a) { return { title: Utils$1.capitalizeFirstLetter(a.name), elem: a }; }); Select.show({ title: name, items: select, onSelect: function onSelect(a) { _this3.toggle(); call(a.elem); }, onBack: function onBack() { _this3.toggle(); } }); }); return elem; } }, { key: "toggle", value: function toggle() { var _this4 = this; var controller = { link: this, toggle: function toggle() { Controller.collectionSet(_this4.render()); Controller.collectionFocus(_this4.last, _this4.render()); _this4.emit('toggle'); }, update: function update() {}, right: function right() { Navigator.move('right'); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else _this4.emit('left'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down');else _this4.emit('down'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else _this4.emit('up'); }, back: this.emit.bind(this, 'back') }; this.emit('controller', controller); Controller.add('full_descr', controller); Controller.toggle('full_descr'); } }, { key: "render", value: function render(js) { return js ? this.html[0] : this.html; } }, { key: "destroy", value: function destroy() { this.body.remove(); this.html.remove(); this.emit('destroy'); } }]); return Descriptiopn; }(Emit); function MetadataChart(data) { var result = { title: Lang.translate('title_metadata'), results: [] }; var fields = [{ name: 'humor', color: '#f7e74a' }, { name: 'violence', color: '#f74a4a' }, { name: 'fear', color: '#e2b2ff' }, { name: 'tension', color: '#45c1ff' }, { name: 'romance', color: '#f74aa3' }, { name: 'sadness', color: '#f7a34a' }, { name: 'pace', color: '#4af74a' }, { name: 'importance', color: '#8ffff6' }, { name: 'action', color: '#ff8124' }, { name: 'sex', color: '#f74af7' }, { name: 'profanity', color: '#ff6262' }]; var chart_data = data.metadata.review || []; chart_data.forEach(function (meter) { var _fields$find; var color = ((_fields$find = fields.find(function (f) { return f.name == meter.name; })) === null || _fields$find === void 0 ? void 0 : _fields$find.color) || '#fff'; var chart = { bars: meter.values.map(function (v) { return (v || 0) / 10 * 100; }), threshold: 70, threshold_color: color }; meter.title = Lang.translate('title_meta_' + meter.name); meter.limit = 10; meter.count = meter.avg; meter.icon = ''; meter.chart = chart; }); result.results = chart_data; Utils$1.extendItemsParams(result.results, { module: Helper$8.toggle(Helper$8.MASK.base, 'Line', 'Chart', 'Icon'), createInstance: function createInstance(item) { return new Register(item); } }); var comp = Utils$1.createInstance(Line$4, result, { module: Helper$b.only('Items', 'Create') }); return comp; } function MetadataTags(data) { var meter = { title: Lang.translate('title_moods'), results: [], params: { items: { view: 10 } } }; var chart_data = data.metadata.moods || []; chart_data.slice(0, 10).forEach(function (key) { meter.results.push({ title: Utils$1.capitalizeFirstLetter(key.name), count: key.percent + '%' }); }); Utils$1.extendItemsParams(meter.results, { module: Helper$8.toggle(Helper$8.MASK.base, 'Line', 'Tag'), createInstance: function createInstance(item) { return new Register(item); } }); var comp = Utils$1.createInstance(Line$4, meter, { module: Helper$b.only('Items', 'Create') }); return comp; } var Line$2 = { onCreate: function onCreate() { var _this = this; this.data.role = this.data.character || this.data.job || Lang.translate('title_actor'); this.html = Template.js('full_person', this.data); this.html.on('visible', function () { Utils$1.imgLoad(_this.html.find('img'), _this.data.profile_path ? TMDB.img(_this.data.profile_path, 'w276_and_h350_face') : _this.data.img || './img/actor.svg', function () { _this.html.addClass('full-person--loaded'); }); }); } }; var About$1 = { onCreate: function onCreate() { var _this = this; this.html = Template.js('person_start', { name: this.data.name, birthday: this.data.birthday ? Utils$1.parseTime(this.data.birthday).full : Lang.translate('player_unknown'), place: this.data.place_of_birth || Lang.translate('player_unknown') }); this.prefix = Template.prefix(this.html, 'person-start'); this.subscribed = Storage.get('person_subscribes_id', '[]').find(function (a) { return a == _this.data.id; }); Utils$1.imgLoad(this.prefix.img, this.data.profile_path ? TMDB.img(this.data.profile_path, 'w500') : this.data.img || 'img/img_broken.svg', function (img) { img.addClass('loaded'); }); if (window.lampa_settings.account_use) this.emit('subscribe', this.subscribed); this.html.find('.button--info').on('hover:enter', function () { if (_this.data.biography) { Modal.open({ title: _this.data.name, size: 'large', html: $('
    ' + _this.data.biography.replace(/\n/g, '
    ') + '
    '), onBack: function onBack() { Modal.close(); Controller.toggle('content'); } }); } else { Noty.show(Lang.translate('empty_title_two')); } }).on('hover:focus hover:enter hover:hover', function (e) { _this.last = e.target; }); if (window.lampa_settings.account_use) { this.html.find('.button--subscribe').on('hover:enter', function () { if (!Account$1.Permit.access) return Account$1.Modal.account(); var subscribes = Storage.get('person_subscribes_id', '[]'); _this.subscribed = subscribes.find(function (a) { return a == _this.data.id; }); if (!_this.subscribed && !Account$1.hasPremium()) return Account$1.Modal.premium(); var person = Arrays.clone(_this.data); // Пихают там всякие иконки и прочее, удаляем лишнее delete person.biography; Lampa.Network.silent(Utils$1.protocol() + object$2.cub_domain + '/api/person/' + (_this.subscribed ? 'unsubscribe' : 'subscribe'), function () { if (_this.subscribed) Arrays.remove(subscribes, _this.data.id);else if (subscribes.indexOf(_this.data.id) == -1) subscribes.push(_this.data.id); Storage.set('person_subscribes_id', subscribes); _this.subscribed = subscribes.find(function (a) { return a == _this.data.id; }); _this.emit('subscribe', _this.subscribed); }, function (err) { if (err.responseJSON && err.responseJSON.code == 555) Account$1.Modal.premium();else Noty.show(Lang.translate('subscribe_error')); }, { person: JSON.stringify(person) }, { headers: { token: Account$1.Permit.token } }); }).on('hover:focus hover:enter hover:hover', function (e) { _this.last = e.target; }); } else this.html.find('.button--subscribe').remove(); }, onSubscribe: function onSubscribe(subscribed) { this.html.find('.button--subscribe svg path:nth-of-type(2)').setAttribute('fill', subscribed ? 'currentColor' : 'transparent'); this.html.find('.button--subscribe span').text(Lang.translate(subscribed ? 'title_unsubscribe' : 'title_subscribe')); } }; var PersonMap = { Line: Line$2, About: About$1, Callback: Callback$1 }; var Person = /*#__PURE__*/function (_Constructor) { _inherits(Person, _Constructor); var _super = _createSuper(Person); function Person() { _classCallCheck(this, Person); return _super.apply(this, arguments); } return Person; }(Constructor(PersonMap)); var Helper$6 = new Mask(Arrays.getKeys(PersonMap)); Helper$6.MASK.base = Helper$6.MASK.none; function Persons(data) { Utils$1.extendItemsParams(data.results, { module: Helper$6.toggle(Helper$6.MASK.base, 'Line', 'Callback'), createInstance: function createInstance(item) { return new Person(item); }, emit: { onEnter: function onEnter(html, item) { router.call('actor', item); } } }); var comp = Utils$1.createInstance(Line$4, data, { module: Helper$b.only('Items', 'Create') }); return comp; } var Line$1 = { onCreate: function onCreate() { var _this = this; var text = (this.data.text || this.data.comment) + ''; text = Utils$1.capitalizeFirstLetter(!this.params.line.full_text && text.length > 120 ? text.slice(0, 120) + '...' : text); this.html = Template.js('full_review', _objectSpread2(_objectSpread2({}, this.data), {}, { text: text })); if (this.data.liked < 0) this.html.addClass('bad--comment'); if (this.params.line.full_text) this.html.addClass('full-review--full-text'); if (this.data.email) { var who = Template.elem('div', { "class": 'full-review__user', children: [Template.elem('div', { "class": 'full-review__user-icon', children: [Template.elem('img', { "class": 'full-review__user-img' })] }), Template.elem('div', { "class": 'full-review__user-email', text: Utils$1.capitalizeFirstLetter(this.data.email) })] }); var like = Template.elem('div', { "class": 'full-review__like', children: [Template.elem('div', { "class": 'full-review__like-icon', children: [Template.js('icon_like')] }), Template.elem('div', { "class": 'full-review__like-counter', text: this.data.liked || '0' })] }); this.html.find('.full-review__footer').html(who).append(like); this.html.on('visible', function () { var img = who.find('img'); Utils$1.imgLoad(img, Utils$1.protocol() + object$2.cub_domain + '/img/profiles/' + _this.data.icon + '.png', function () { who.addClass('loaded'); }, function () { img.src = './img/actor.svg'; }); }); } }, onUpdateLiked: function onUpdateLiked(add) { this.data.liked = (this.data.liked || 0) + add; this.html.find('.full-review__like-counter').text(this.data.liked); } }; function containsLongWords(str) { var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 15; var any = false; str.split(/\s/).map(function (a) { if (a.length >= length) any = true; }); return any; } function containsFiveWords(str) { var words = str.split(/\s/); var count = 0; words.map(function (a) { if (a.length >= 5) count++; }); return count; } function filter(text) { var err = 0; if (/\d{4,}/g.test(text)) err = 1;else if (!/[а-яА-ЯёЁ]{5,}/.test(text)) err = 2;else if (!/[.,:;!?]/.test(text)) err = 3;else if (/[*%$#_+=|^&]/.test(text)) err = 4;else if (containsFiveWords(text) < 5) err = 5;else if (containsLongWords(text)) err = 6;else if (text.length > 300) err = 7; return err; } var Add = { onCreate: function onCreate() { var _this = this; this.html = Template.elem('div', { "class": 'full-review-add selector' }); this.html.on('hover:enter', function () { if (_this.added) return Noty.show(Lang.translate('account_discuss_added_ready')); if (Account$1.Permit.access) { var add_value = ''; var controller = Controller.enabled().name; if (Platform.tv()) { var html = Template.js('modal_qr', { qr_text: Lang.translate('account_discuss_add_qr'), title: Lang.translate('account_discuss_add_title') }); html.addClass('layer--' + (Platform.mouse() ? 'wheight' : 'height')); html.find('.account-modal-split__text').html(Lang.translate('account_discuss_add_text')); var code = html.find('.account-modal-split__qr-code'); var url = [Activity.active().source, Activity.active().method, Activity.active().id].join('/'); Utils$1.qrcode('https://' + object$2.cub_site + '/addcomment/' + url, code, function () { code.remove(); }); Modal.open({ title: '', html: $(html), size: 'full', scroll: { nopadding: true }, onBack: function onBack() { Modal.close(); Controller.toggle(controller); } }); } else { var rules_html = Template.js('discuss_rules'); document.body.append(rules_html); var keyboard = Input.edit({ title: '', value: add_value, nosave: true, textarea: true, align: Platform.screen('mobile') ? 'top' : 'center', keyboard: 'integrate' }, function (new_value) { rules_html.remove(); add_value = new_value; if (new_value) { Account$1.Api.load('discuss/add', {}, { id: [Activity.active().method, Activity.active().id].join('_'), comment: new_value, lang: Storage.field('language') }).then(function (data) { _this.added = true; Bell.push({ text: Lang.translate('account_discuss_added') }); })["catch"](function (e) { Noty.show(Lampa.Network.errorJSON(e).text || Lang.translate('network_500'), { time: 5000 }); }); } Controller.toggle(controller); }); var keypad = $('.simple-keyboard'); var helper = $('
    '); if (keypad.find('.simple-keyboard-input').length) { keypad.after(helper); keyboard.listener.follow('change', function (event) { var code = filter(event.value.trim()); helper.toggleClass('hide', !Boolean(code)).text(Lang.translate('discuss_rules_rule_' + code)); }); } } } else { Account$1.Modal.account(); } }); } }; var Read = { onCreate: function onCreate() { var _this = this; this.html.on('hover:enter', function () { var items = []; var voited = Storage.cache('discuss_voited', 100, []); var contoller = Controller.enabled().name; if (voited.indexOf(_this.data.id) == -1 && Account$1.Permit.access) { items = [{ separator: true, title: Lang.translate('title_action') }, { title: '+1 ' + Lang.translate('title_like'), like: 1 }, { title: Lang.translate('reactions_shit'), like: -1 }]; } Select.show({ title: Utils$1.capitalizeFirstLetter(_this.data.email), items: items, onFullDraw: function onFullDraw(select_scroll) { if (_this.params.line.full_text) return; select_scroll.body(true).prepend(Template.elem('div', { "class": 'selectbox__text selector', children: [Template.elem('div', { text: Utils$1.capitalizeFirstLetter(_this.data.comment) })] })); }, onSelect: function onSelect(item) { Controller.toggle(contoller); Account$1.Api.load('discuss/voite', {}, { id: _this.data.id, like: item.like }).then(function () { Storage.add('discuss_voited', _this.data.id); _this.emit('updateLiked', item.like); Bell.push({ text: Lang.translate('discuss_voited') }); })["catch"](function () {}); }, onBack: function onBack() { Controller.toggle(contoller); } }); }); } }; var DiscussMap = { Line: Line$1, Add: Add, Read: Read, Callback: Callback$1 }; var Discuss = /*#__PURE__*/function (_Constructor) { _inherits(Discuss, _Constructor); var _super = _createSuper(Discuss); function Discuss(data) { var _this; _classCallCheck(this, Discuss); _this = _super.call(this, data); Arrays.extend(data.params, { line: { full_text: false } }); return _this; } return Discuss; }(Constructor(DiscussMap)); var Helper$5 = new Mask(Arrays.getKeys(DiscussMap)); Helper$5.MASK.base = Helper$5.only('Callback'); function Discussions(data) { Utils$1.extendItemsParams(data.results, { module: Helper$5.only('Line', 'Read'), createInstance: function createInstance(item) { return new Discuss(item); } }); Arrays.insert(data.results, 0, { params: { module: Helper$5.only('Add'), createInstance: function createInstance(item) { return new Discuss(item); } } }); var comp = Utils$1.createInstance(Line$4, data, { module: Helper$b.only('Items', 'Create', 'More') }); comp.use({ onMore: router.call.bind(router, 'discuss', data.movie) }); return comp; } function Episodes(data) { Utils$1.extendItemsParams(data.results, { module: Helper$a.only('Small', 'Mark', 'Callback'), createInstance: function createInstance(item) { return new Episode(item); } }); data.results.reverse(); data.results.filter(function (e) { return e.comeing; }).forEach(function (e) { e.params.module = Helper$a.only('SmallNext'); }); if (!data.results.length) { data.results.push({ episode_number: 1, season_number: data.movie.number_of_seasons || 1, air_date: '', name: Lang.translate('title_anons'), comeing: true, params: { module: Helper$a.only('SmallNext'), createInstance: function createInstance(item) { return new Episode(item); } } }); } var comp = Utils$1.createInstance(Line$4, data, { module: Helper$b.only('Items', 'Create', 'MoreFirst') }); comp.use({ onMore: router.call.bind(router, 'episodes', data.movie) }); return comp; } function Cards(data) { var comp = Utils$1.createInstance(Line$4, data, { module: Helper$b.toggle(Helper$b.MASK.base, 'More') }); comp.use({ onInstance: function onInstance(item, data) { item.use({ onEnter: router.call.bind(router, 'full', data) }); } }); return comp; } var components = { start: Start, description: Descriptiopn, metadata_chart: MetadataChart, metadata_tags: MetadataTags, persons: Persons, cards: Cards, discuss: Discussions, episodes: Episodes }; /** * Компонент "Карточка фильма/сериала" * @param {*} object */ function component$m(object) { var comp = Utils$1.createInstance(Full$1, object, { module: Helper$7.only('Items', 'Empty', 'Callback'), empty: { cub_button: true } }); comp.use({ onCreate: function onCreate() { var _this = this; this.tv = Platform.screen('tv'); this.view = 3; this.rows = ['start', 'description']; this.html.addClass('layer--wheight'); Api.full(object, function (data) { if (!data.movie) return _this.emit('error', { empty: true }); // Добавляем в пропсы данные _this.props.set(data); // Проверяем по ключевым словам, есть ли в фильме ЛГБТ тематика var key_tags = data.movie.keywords ? data.movie.keywords.results || data.movie.keywords.keywords : []; var lgbt_block = Storage.field('lgbt_content_block') || vpn.is(['ru', 'by']); if (lgbt_block && key_tags && key_tags.find && key_tags.length && window.lampa_settings.lgbt) { Keys.lgbt.forEach(function (keyword) { if (key_tags.find(function (k) { return k.name.toLowerCase() == keyword; })) data.movie.lgbt = 'keyword (' + keyword + ')'; }); } // Если фильм не помечен как ЛГБТ, но есть в списке блокировки ЛГБТ, то помечаем его if (lgbt_block && window.lampa_settings.lgbt && !data.movie.lgbt) { if (window.lampa_settings.lgbt[data.movie.id + '_' + (data.movie.first_air_date ? 'tv' : 'movie')]) data.movie.lgbt = 'list'; } if (data.movie.blocked || data.movie.lgbt) return _this.emit('error', { blocked: true, lgbt: data.movie.lgbt }); // Для плагинов которые используют Activity.active().card object.card = data.movie; // Проверяем можно ли показывать полную карточку детям var watch = Utils$1.canWatchChildren(TMDB.parsePG(data.movie), permit.profile.age); // Ищем по ключевым словам, есть ли в фильме тематика для взрослых var adult_block = key_tags && key_tags.find && key_tags.length ? key_tags.find(function (key) { return Keys.adult.find(function (word) { return key.name.toLowerCase().indexOf(word) >= 0; }); }) : false; if (Storage.field('adult_content_view')) adult_block = false; // Если фильм помечен как для взрослых, то добавляем это в данные фильма if (adult_block) data.movie.adult = true; // Отправляем событие, что началась загрузка полной карточки if (watch) Lampa.Listener.send('full', { link: _this, type: 'start', props: _this.props, body: $(_this.html), object: object, data: data }); if (data.metadata && data.metadata && data.metadata.status && data.metadata.status == 'completed') { _this.rows.push(['metadata_chart', { movie: data.movie, metadata: data.metadata }]); if (Lang.selected(['ru', 'uk', 'be'])) { _this.rows.push(['metadata_tags', { movie: data.movie, metadata: data.metadata }]); } } // Создаем эпизоды if (!adult_block && data.episodes && data.episodes.episodes) { var episodes = data.episodes.episodes; // Если сериал многосезонный, то выбираем нужный сезон if (data.episodes.seasons_count && data.episodes.seasons_count > 1 && data.episodes.episodes_original) { // Парсим эпизоды по сезонам var seasons = Utils$1.splitEpisodesIntoSeasons(data.episodes.episodes_original); // Выбираем нужный сезон episodes = seasons[data.episodes.seasons_count] || seasons[1]; // Сохраняем номер сезона data.episodes.name = Lang.translate('torrent_serial_season') + ' ' + data.episodes.seasons_count; } var today = new Date(); var date = [today.getFullYear(), today.getMonth() + 1, today.getDate()].join('-'); var time = Utils$1.parseToDate(date).getTime(); var cameout = episodes.filter(function (a) { return a.air_date; }).filter(function (e) { return Utils$1.parseToDate(e.air_date).getTime() <= time; }); var comeing = episodes.filter(function (a) { return a.air_date; }).filter(function (e) { return Utils$1.parseToDate(e.air_date).getTime() > time; }); comeing.forEach(function (e) { return e.comeing = true; }); if (comeing.length) cameout = cameout.concat(comeing.slice(0, 1)); cameout.forEach(function (episode) { return episode.original_name = data.movie.original_name || data.movie.name; }); _this.rows.push(['episodes', { movie: data.movie, title: data.episodes.name || Lang.translate('full_series_release'), results: cameout }]); } // Создаем режиссеров if (!adult_block && data.persons && data.persons.crew && data.persons.crew.length) { var directors = data.persons.crew.filter(function (member) { return member.job === 'Director'; }); directors.length && _this.rows.push(['persons', { results: directors, title: Lang.translate('title_producer') }]); } // Создаем актеров if (!adult_block && data.persons && data.persons.cast && data.persons.cast.length) _this.rows.push(['persons', { results: data.persons.cast, title: Lang.translate('title_actors') }]); // Создаем отзывы if (!adult_block && data.discuss) Arrays.insert(_this.rows, data.discuss.result.length ? 2 : _this.rows.length, ['discuss', _objectSpread2(_objectSpread2({}, data.discuss), {}, { movie: data.movie, title: Lang.translate('title_comments'), results: data.discuss.result || [] })]); // Создаем коллекцию if (!adult_block && data.collection && data.collection.results && data.collection.results.length) { data.collection.title = Lang.translate('title_collection'); // Сортируем по году от меньшего к большему data.collection.results.sort(function (a, b) { var yearA = a.release_date ? a.release_date.split('-')[0] : a.first_air_date ? a.first_air_date.split('-')[0] : '0'; var yearB = b.release_date ? b.release_date.split('-')[0] : b.first_air_date ? b.first_air_date.split('-')[0] : '0'; return parseInt(yearA) - parseInt(yearB); }); _this.rows.push(['cards', data.collection]); } // Создаем рекомендации if (!adult_block && data.recomend && data.recomend.results && data.recomend.results.length) { data.recomend.title = Lang.translate('title_recomendations'); _this.rows.push(['cards', data.recomend]); } // Создаем похожие if (!adult_block && data.simular && data.simular.results && data.simular.results.length) { data.simular.title = Lang.translate('title_similar'); _this.rows.push(['cards', data.simular]); } // Добавляем картинку для фона _this.html.prepend(Template.elem('img', { "class": 'full-start__background' })); // Добавляем фоновую картинку var background = data.movie.backdrop_path ? Api.img(data.movie.backdrop_path, 'w1280') : data.movie.background_image ? data.movie.background_image : ''; if (window.innerWidth > 790 && background && !Storage.field('light_version')) { Utils$1.imgLoad(_this.html.find('.full-start__background'), background, function (img) { return img.addClass('loaded'); }); } else _this.html.find('.full-start__background').remove(); // Создаем все компоненты _this.build(_this.rows.slice(0, _this.view)); // Обновляем расписание TimeTable.update(data.movie); // Отправляем событие, что полная карточка загружена if (watch) Lampa.Listener.send('full', { link: _this, type: 'complite', props: _this.props, body: $(_this.html), object: object, data: data }); _this.activity.loader(false); // Группируем кнопки после полной загрузки if (_this.items.length) _this.items[0].emit('groupButtons'); _this.activity.toggle(); }, this.emit.bind(this, 'error')); }, onBuild: function onBuild() { this.scroll.onScroll = this.emit.bind(this, 'scroll'); // Для планшетов в вертикальном режиме принудительно вызываем скролл if (!this.tv) this.emit('scroll', 0); }, onStart: function onStart() { this.props.get('movie') && Background.immediately(Utils$1.cardImgBackgroundBlur(this.props.get('movie'))); }, onScroll: function onScroll(position) { var _this$html$find; var size = this.tv ? Math.min(this.active + this.view, this.rows.length) : this.rows.length; var add = this.rows.slice(this.items.length, size); if (add.length) { this.fragment = document.createDocumentFragment(); add.forEach(this.emit.bind(this, 'createAndAppend')); this.scroll.append(this.fragment); } (_this$html$find = this.html.find('.full-start__background')) === null || _this$html$find === void 0 ? void 0 : _this$html$find.toggleClass('dim', position > 0); Layer.visible(this.scroll.render()); }, onCreateAndAppend: function onCreateAndAppend(component) { var name = Arrays.isArray(component) ? component[0] : component; var data = Arrays.isArray(component) ? component[1] : this.props.all(); var item = new components[name](data); this.emit('instance', item); item.create(); this.emit('append', item); // Отправляем событие, что компонент создан Lampa.Listener.send('full', { link: this, type: 'build', props: this.props, body: $(this.html), name: name, item: item, data: data }); }, onError: function onError(status) { var params = this.params.empty; var dmca = Utils$1.dcma(this.object.method, this.object.id); var lgbt = this.props.get('movie') && this.props.get('movie').lgbt; if (dmca || status.blocked) { params.title = Lang.translate('dmca_title'); params.descr = Lang.translate('dmca_descr'); params.noicon = true; } params.info_button = [['Movie id', this.object.id], ['DMCA', dmca ? 'Yes' : 'No'], ['LGBT', lgbt ? 'Yes, ' + lgbt : 'No']]; if (lgbt) { params.title = Lang.translate('dmca_title_lgbt'); params.descr = Lang.translate('dmca_descr_lgbt'); params.info_button.push(['Title', this.props.get('movie').title || this.props.get('movie').name || '---']); } // Вызываем пустой экран this.empty(status); } }); // Переопределяем билд comp.build = function (data) { this.emit('build', data); }; return comp; } var Base = /*#__PURE__*/function (_Emit) { _inherits(Base, _Emit); var _super = _createSuper(Base); function Base(object) { var _this; _classCallCheck(this, Base); _this = _super.call(this); Arrays.extend(object, { params: {} }); _this.object = object; _this.params = object.params; _this.scroll = new Scroll({ mask: true, over: true, step: 250, end_ratio: 2 }); _this.html = document.createElement('div'); _this.body = document.createElement('div'); return _this; } _createClass(Base, [{ key: "create", value: function create() { var _this2 = this; this.activity.loader(true); this.scroll.minus(); this.scroll.onWheel = function (step) { if (!Controller.own(_this2)) _this2.start(); Navigator.move(step > 0 ? 'down' : 'up'); }; this.scroll.append(this.body); this.html.append(this.scroll.render(true)); this.emit('create'); } }, { key: "build", value: function build(data) { this.emit('build', data); this.activity.loader(false); this.activity.toggle(); } }, { key: "empty", value: function empty(status) { this.scroll.nopadding(); this.emit('empty', status); this.activity.loader(false); this.activity.toggle(); } }, { key: "start", value: function start() { var _this3 = this; this.emit('start'); var controller = { link: this, invisible: true, toggle: function toggle() { _this3.scroll.restorePosition(); Controller.collectionSet(_this3.scroll.render(true)); if (Platform.screen('tv')) Controller.collectionFocus(_this3.last || false, _this3.scroll.render(true)); _this3.emit('toggle'); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else Controller.toggle('menu'); }, right: function right() { if (Navigator.canmove('right')) Navigator.move('right');else _this3.emit('right'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('head'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down'); }, back: function back() { Activity.backward(); } }; this.emit('controller', controller); Controller.add('content', controller); Controller.toggle('content'); } }, { key: "pause", value: function pause() { this.emit('pause'); } }, { key: "resize", value: function resize() { this.emit('resize'); } }, { key: "render", value: function render(js) { return js ? this.html : $(this.html); } }, { key: "destroy", value: function destroy() { this.scroll.destroy(); this.html.remove(); this.body.remove(); this.emit('destroy'); } }]); return Base; }(Emit); var Items = { onInit: function onInit() { this.items = []; this.pages = {}; this.active = 0; this.added = 0; this.screen = Platform.screen('tv'); this.loaded = []; Arrays.extend(this.params, { items: { mapping: 'grid', cols: 6, limit_view: 6, limit_collection: 36 } }); this.total_pages = 1; this.limit_view = this.params.items.limit_view; this.limit_collection = this.params.items.limit_collection; }, onCreate: function onCreate() { var _this = this; this.scroll.onScroll = this.emit.bind(this, 'scroll'); this.scroll.onAnimateEnd = function () { // Скрываем/показываем страницы _this.emit('pageView'); // Обновляем навигатор _this.emit('scroll'); }; this.body.addClass('mapping--' + this.params.items.mapping); this.params.items.mapping == 'grid' && this.body.addClass('cols--' + this.params.items.cols); }, onAppend: function onAppend(item, element) { var _this2 = this; var render = item.render(true); render.on('hover:touch hover:enter hover:focus', function () { _this2.last = render; _this2.active = _this2.items.indexOf(item); }); render.on('hover:focus', function () { _this2.scroll.update(render, _this2.params.items.mapping == 'list'); }); if (element.params.on && _typeof(element.params.on) == 'object') { var _loop = function _loop(e) { render.on(e, function () { element.params.on[e].call(_this2, item, element); }); }; for (var e in element.params.on) { _loop(e); } } this.frament.appendChild(render); this.items.push(item); this.added++; var page = Math.ceil(this.added / 60); // 60 - количество элементов на странице if (!this.pages[page]) this.pages[page] = { items: [] }; this.pages[page].items.push(item); }, onBuild: function onBuild(data) { if (!data.results.length) return this.empty(); this.total_pages = data.total_pages || 1; this.loaded.push(data.results); this.emit('pushLoaded'); this.emit('scroll'); }, onPushLoaded: function onPushLoaded() { var add = this.loaded.shift(); if (add && add.length) { this.frament = document.createDocumentFragment(); add.forEach(this.emit.bind(this, 'createAndAppend')); this.body.append(this.frament); } }, onPageView: function onPageView() { var item = this.items[this.active]; var page = 1; var anyscroll; var item_position = 0; var scroll_position = this.screen ? 0 : this.scroll.render(true).scrollTop; if (item) { for (var p in this.pages) { this.pages[p].items.find(function (i) { return i.data == item.data; }) && (page = p); } } // Надо получить позицию до изменений if (this.last) item_position = this.screen ? this.last.getBoundingClientRect().top : this.last.offsetTop; for (var _p in this.pages) { var current = this.pages[_p]; var visible = _p >= page - 1 && _p - 1 <= page; // Если страница видна, но была удалена, то вставляем её обратно if (visible && current.removed) { (function () { current.removed = false; var frag = document.createDocumentFragment(); current.items.forEach(function (i) { return frag.appendChild(i.render(true)); }); current.items.forEach(function (i) { return i.render(true).style.visibility = 'visible'; }); current.placeholder.replaceWith(frag); current.placeholder = null; anyscroll = true; })(); } // Если страница не видна, то удаляем её из DOM else if (!visible && !current.removed) { current.removed = true; current.placeholder = document.createElement('div'); current.placeholder.style.display = 'none'; // current.items[0] Почему-то не всегда есть, баг на webos if (current.items[0] && current.items[0].render(true)) current.items[0].render(true).after(current.placeholder); current.items.forEach(function (i) { return i.render(true).remove(); }); anyscroll = true; } } // Если было удаление или добавление страниц, то надо подвинуть скролл if (anyscroll && this.last) { if (this.screen) { this.scroll.shift(this.last.getBoundingClientRect().top - item_position); } else { this.scroll.render(true).scrollTop = scroll_position + (this.last.offsetTop - item_position); } } }, onScroll: function onScroll() { // Грузим элементы которые ожидают своей очереди this.emit('pushLoaded'); if (Controller.own(this)) { Navigator.setCollection(this.items.slice(Math.max(0, this.active - this.limit_collection), this.active + this.limit_collection).map(function (c) { return c.render(true); })); Navigator.focused(this.last); } Layer.visible(this.scroll.render(true)); }, onResize: function onResize() { if (this.last) this.scroll.update(this.last, this.params.items.mapping == 'list'); }, onDestroy: function onDestroy() { Arrays.destroy(this.items); this.pages = {}; this.items = []; this.loaded = []; } }; var Create = { onCreateAndAppend: function onCreateAndAppend(element) { try { var item = Utils$1.createInstance(Card$3, element); this.emit('instance', item, element); item.create(); this.emit('append', item, element); } catch (e) { console.warn('Warning', 'onCreateAndAppend error:', e.message, e.stack); } } }; var Next = { onInit: function onInit() { this.next_wait; this.builded_time = Date.now(); }, onBuild: function onBuild() { this.scroll.onEnd = this.emit.bind(this, 'loadNext'); this.builded_time = Date.now(); }, onLoadNext: function onLoadNext() { var _this = this; if (!this.next_wait && this.items.length && this.builded_time < Date.now() - 1000 && this.object.page < this.total_pages) { this.next_wait = true; this.object.page++; this.emit('next', function (new_data) { _this.next_wait = false; if (!_this.items.length || _this.destroyed) return; var split_total = Math.ceil(new_data.results.length / _this.limit_view); // Разбиваем на части, чтобы не лагал браузер for (var i = 0; i < split_total; i++) { _this.loaded.push(new_data.results.slice(i * _this.limit_view, (i + 1) * _this.limit_view)); } // Если нет анимации у скрола, то можно грузить сразу if (!_this.scroll.animated()) _this.scroll.onAnimateEnd();else _this.emit('scroll'); }, function () { _this.next_wait = false; }); } } }; /** * Исследователь * @param {object} object - объект с параметрами * @param {object} object.movie - информация о фильме/сериале * @param {string} object.source - источник фильма/сериала * @param {boolean} object.params.noinfo - не показывать информацию о фильме/сериале * @returns {Explorer} - экземпляр класса Explorer */ var Explorer$1 = /*#__PURE__*/function (_Emit) { _inherits(Explorer, _Emit); var _super = _createSuper(Explorer); function Explorer(object) { var _this; _classCallCheck(this, Explorer); _this = _super.call(this); Arrays.extend(object, { params: {} }); _this.object = object; _this.params = object.params || {}; _this.html = Template.get('explorer', {}); _this.scroll = new Scroll({ mask: true, over: true }); _this.card = _this.html.find('.explorer__card').clone(); _this.html.find('.explorer__left').empty().append(_this.scroll.render()); _this.html.find('.explorer__files-head').toggleClass('hide', true); _this.scroll.append(_this.card); _this.scroll.minus(); _this.movie(); _this.emit('init'); return _this; } _createClass(Explorer, [{ key: "movie", value: function movie() { var _this2 = this; var movie = this.object.movie || this.object.card || {}; Arrays.extend(movie, { title: 'Фильм не найден', original_title: '', img: './img/img_broken.svg', genres: [{ name: 'Комедия' }, { name: 'Боевик' }, { name: 'Драма' }], overview: 'Этот фильм мог бы быть интересным, но, к сожалению, нет данных о нём.', vote_average: 8, production_countries: [{ iso_3166_1: 'US' }] }); if (movie.id) { this.html.find('.selector').on('hover:enter', function () { if (Activity.all().length > 1) { Activity.back(); } else { Activity.push({ url: movie.url, component: 'full', id: movie.id, method: movie.name ? 'tv' : 'movie', card: movie, source: movie.source || _this2.object.source || 'tmdb' }); } }); } var year = ((movie.release_date || movie.first_air_date || new Date().getFullYear() + '-01-01') + '').slice(0, 4); var pg = TMDB.parsePG(movie); var countries = TMDB.parseCountries(movie); var rate = parseFloat((movie.vote_average || 0) + ''); var title = movie.title || movie.name || ''; var genres = (movie.genres || [{ name: '' }]).slice(0, 3).map(function (a) { return Utils$1.capitalizeFirstLetter(a.name); }); this.html.find('.explorer-card__head-create').text(year + (countries.length ? ' - ' + countries[0] : '')).toggleClass('hide', Boolean(year == '0000')); this.html.find('.explorer-card__head-rate').toggleClass('hide', !Boolean(rate > 0)).find('span').text(rate.toFixed(1)); this.html.find('.explorer-card__title').text(title).toggleClass('small', Boolean(title.length > 50)); this.html.find('.explorer-card__descr').text(movie.overview || ''); this.html.find('.explorer-card__genres').text(genres.join(', ')); if (pg) this.html.find('.explorer-card__head-body').append('
    ' + pg + '
    '); if (this.params.noinfo) this.html.addClass('explorer--fullsize'); this.img = this.html.find('.explorer-card__head-img > img')[0]; this.img.style.opacity = 0; this.img.onload = function () { _this2.img.style.opacity = 1; }; this.img.onerror = function () { _this2.img.src = './img/img_broken.svg'; }; this.img.src = movie.poster_path ? TMDB.img(movie.poster_path, 'w300') : movie.img || './img/img_broken.svg'; this.emit('movie'); } }, { key: "appendFiles", value: function appendFiles(element) { this.html.find('.explorer__files-body').append(element); } }, { key: "appendLeft", value: function appendLeft(element) { this.scroll.append(element); } }, { key: "appendHead", value: function appendHead(element) { this.html.find('.explorer__files-head').append(element).toggleClass('hide', false); } }, { key: "render", value: function render(js) { return js ? this.html[0] : this.html; } }, { key: "clearFiles", value: function clearFiles() { this.html.find('.explorer__files-body').empty(); } }, { key: "clearLeft", value: function clearLeft() { this.scroll.clear(); } }, { key: "clearHead", value: function clearHead() { this.html.find('.explorer__files-head').empty().toggleClass('hide', true); } }, { key: "toggle", value: function toggle() { var _this3 = this; var controller = { link: this, toggle: function toggle() { Controller.collectionSet(_this3.scroll.render(true)); Controller.collectionFocus(false, _this3.scroll.render(true)); }, left: function left() { Controller.toggle('menu'); }, up: function up() { if (_this3.scroll.position() == 0) Controller.toggle('head');else if (_this3.scroll.position() > -170) { _this3.scroll.wheel(_this3.scroll.position()); Controller.toggle('explorer'); } else _this3.scroll.wheel(-150); }, right: function right() { Controller.toggle('content'); }, down: function down() { Controller.clear(); _this3.scroll.wheel(150); }, back: function back() { Activity.backward(); } }; this.emit('controller', controller); Controller.add('explorer', controller); Controller.toggle('explorer'); } }, { key: "destroy", value: function destroy() { this.scroll.destroy(); this.html.remove(); this.img.onerror = function () {}; this.img.src = ''; this.emit('destroy'); } }]); return Explorer; }(Emit); var Explorer = { onInit: function onInit() { this.explorer = new Explorer$1(this.object); }, onCreate: function onCreate() { this.explorer.appendFiles(this.scroll.render(true)); this.scroll.minus(this.explorer.render(true).find('.explorer__files-head')); this.html.append(this.explorer.render(true)); }, onController: function onController(controller) { var _this = this; controller.left = function () { if (Navigator.canmove('left')) Navigator.move('left');else _this.explorer.toggle(); }; }, onEmpty: function onEmpty() { var _this2 = this; if (this.empty_class) { this.empty_class.use({ onController: function onController(controller) { controller.left = function () { if (Navigator.canmove('left')) Navigator.move('left');else _this2.explorer.toggle(); }; } }); } } }; var Loading = { onCreate: function onCreate() { this.activity.loader(false); Arrays.extend(this.params, { loading: { icon: 'card' } }); var tpl = Template.get('ai_search_animation', { icon: Template.string('icon_' + this.params.loading.icon) }); this.loading = $('
    '); this.loading.append(tpl); this.scroll.append(this.loading); }, onBuild: function onBuild() { this.loading.remove(); }, onEmpty: function onEmpty() { this.loading.remove(); } }; function getSmartPages(current, total) { var maxButtons = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 7; var pages = []; // всегда добавляем первую и последнюю страницу pages.push(1); pages.push(total); // добавляем текущую страницу pages.push(current); // добавляем несколько вокруг текущей for (var i = 1; i <= 2; i++) { if (current - i > 1) pages.push(current - i); if (current + i < total) pages.push(current + i); } // добавляем промежуточные диапазоны (каждые ~10%) var steps = Math.max(2, Math.floor(total / (maxButtons - 2))); for (var _i = steps; _i < total; _i += steps) { pages.push(Math.round(_i)); } // удаляем дубликаты pages = Arrays.unique(pages); // сортируем и возвращаем массив с "..." где пропуски var sorted = pages.sort(function (a, b) { return a - b; }); return sorted; } var Pagination = { onBuild: function onBuild() { this.navigator = Template.elem('div', { "class": 'head__navigator' }); this.navigator.on('click', this.emit.bind(this, 'right')); }, onStart: function onStart() { if (this.total_pages > 5 && this.navigator) Head.addElement(this.navigator); }, onPause: function onPause() { this.navigator && this.navigator.remove(); }, onScroll: function onScroll() { this.navigator && this.navigator.text((this.object.page || 1) + ' / ' + (this.total_pages || 1)); }, onRight: function onRight() { var _this = this; if (!this.navigator) return; var controller = Controller.enabled().name; var current_page = parseInt(this.object.page || 1); var pages = getSmartPages(current_page, parseInt(this.total_pages || 1), 7); pages = pages.map(function (page) { return { title: Lang.translate('title_page') + ' ' + page, page: page, selected: page === current_page }; }); Select.show({ title: Lang.translate('title_navigation'), items: pages, onSelect: function onSelect(a) { _this.object.page = a.page; Controller.toggle(controller); _this.activity.refresh(); }, onBack: function onBack() { Controller.toggle(controller); } }); }, onDestroy: function onDestroy() { this.navigator && this.navigator.remove(); } }; var CategoryMap = { Items: Items, Create: Create, Next: Next, Empty: Simple, Explorer: Explorer, Loading: Loading, Pagination: Pagination }; var Helper$4 = new Mask(Arrays.getKeys(CategoryMap)); Helper$4.MASK.base = Helper$4.except('Loading', 'Explorer', 'Pagination'); var Full = /*#__PURE__*/function (_Base) { _inherits(Full, _Base); var _super = _createSuper(Full); function Full(object) { var _this; _classCallCheck(this, Full); _this = _super.call(this, object); var module = typeof _this.params.module !== 'undefined' ? _this.params.module : Helper$4.MASK.base; Helper$4.getNames(module).map(function (name) { return CategoryMap[name]; }).forEach(function (mod) { return _this.use(mod); }); _this.emit('init'); return _this; } return Full; }(Base); function component$l(object) { var comp = Utils$1.createInstance(Full, object, { module: Helper$4.toggle(Helper$4.MASK.base, 'Pagination') }); comp.use({ onCreate: function onCreate() { Api.list(object, this.build.bind(this), this.empty.bind(this)); }, onNext: function onNext(resolve, reject) { Api.list(object, resolve.bind(this), reject.bind(this)); }, onInstance: function onInstance(item, data) { item.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); return comp; } function component$k(object) { var comp = Utils$1.createInstance(Full$1, object); var next = null; comp.use({ onCreate: function onCreate() { var nextCall = Api.category(object, this.build.bind(this), this.empty.bind(this)); if (typeof nextCall == 'function') next = nextCall; }, onNext: function onNext(resolve, reject) { if (next) { next(resolve.bind(this), reject.bind(this)); } else reject.call(this); }, onInstance: function onInstance(item, data) { item.use({ onMore: router.call.bind(router, 'category_full', _objectSpread2(_objectSpread2({}, data), {}, { genres: object.genres })), onInstance: function onInstance(card, data) { card.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); } }); return comp; } function component$j(object) { var comp = Utils$1.createInstance(Full$1, object); comp.use({ onCreate: function onCreate() { var _this = this; Api.person(object, function (data) { if (data.person) { var lines = []; lines.push({ results: [], params: { module: Helper$b.toggle(Helper$b.MASK.none, 'Event'), emit: { onCreate: function onCreate() { this.person = Utils$1.createInstance(Person, data.person, { module: Helper$6.only('About') }); this.person.create(); this.scroll.append(this.person.render(true)); }, onToggle: function onToggle() { data.person.profile_path && Background.change(Api.img(data.person.profile_path, 'w200')); }, onDestroy: function onDestroy() { var _this$person; (_this$person = this.person) === null || _this$person === void 0 ? void 0 : _this$person.destroy(); } } } }); if (data.credits && data.credits.knownFor && data.credits.knownFor.length > 0) { for (var i = 0; i < data.credits.knownFor.length; i++) { var departament = data.credits.knownFor[i]; var credits = departament.credits.map(function (a) { a.time_sort = new Date(a.first_air_date || a.release_date || '').getTime(); return a; }); credits.sort(function (a, b) { return b.time_sort - a.time_sort; }); if (credits.length > 0) { lines.push({ title: departament.name + ' (' + credits.length + ')', results: credits, params: { module: Helper$b.toggle(Helper$b.MASK.base, 'More', 'Event') } }); } } } if (lines.length) _this.build(lines);else _this.empty(); } else { _this.empty(); } }, this.empty.bind(this)); }, onInstance: function onInstance(item) { item.use({ onInstance: function onInstance(card, data) { card.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); } }); return comp; } var Router = { onEmpty: function onEmpty(e) { var _this = this; Arrays.extend(this.params, { empty: { width: 'medium', buttons: [] } }); var params = this.params.empty; if (params.router == 'subscribe') { params.title = Lang.translate('empty_subscribes_title'); params.descr = Lang.translate('empty_subscribes_text'); params.icon = Template.string('icon_empty_subscribe'); } if (params.router == 'bookmarks') { params.title = Lang.translate('empty_bookmark_title'); params.descr = Lang.translate('empty_bookmark_text'); params.icon = Template.string('icon_empty_bookmarks'); } if (params.router == 'favorites') { params.title = Lang.translate(params.type == 'history' ? 'empty_history_title' : 'empty_bookmark_title'); params.icon = Template.string(params.type == 'history' ? 'icon_empty_history' : 'icon_empty_bookmarks'); if (params.type == 'history') params.descr = Lang.translate('empty_history_text');else params.descr = Lang.translate('empty_bookmark_text'); } if (params.router == 'mytorrents') { params.title = Lang.translate('empty_mytorrents_title'); params.descr = Lang.translate('empty_mytorrents_text'); params.icon = Template.string('icon_empty_torrents'); } if (params.account && !permit.token) { params.buttons.push({ title: Lang.translate('settings_cub_signin_button'), onEnter: function onEnter() { Device.login(_this.start.bind(_this)); } }); } } }; /** * Компонент избранного, просмотр папки или истории * @param {*} object * @returns */ function component$i(object) { var comp = Utils$1.createInstance(Full, object, { module: Helper$4.toggle(Helper$4.MASK.base, 'Pagination'), empty: { type: object.type, router: 'favorites' } }); comp.use(Router, 0); comp.use({ onCreate: function onCreate() { var _this = this; Api.favorite(object, function (data) { // Ой все, и так сработает :) setTimeout(_this.build.bind(_this, data), 10); }, this.empty.bind(this)); }, onNext: function onNext(resolve, reject) { Api.favorite(object, resolve.bind(this), reject.bind(this)); }, onInstance: function onInstance(item, data) { item.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); return comp; } /** * Отображает полноэкранный ввод текста с виртуальной клавиатурой * @param {object} [params] - параметры * @param {string} [params.input] - начальный текст * @param {function} params.onSearch - вызывается при подтверждении ввода * @param {function} params.onBack - вызывается при отмене ввода * @return {void} */ function SearchInput() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var search = Template.get('search_box'); var input = ''; function destroy() { $('body').toggleClass('ambience--enable', false); keyboard.destroy(); search.remove(); search = null; } function back() { destroy(); params.onBack(); } function enter() { destroy(); params.onSearch(input); } function change(text) { input = text; if (input) { search.find('.search-box__input').toggleClass('filled', true).html(input.replace(/\s/g, ' ')); } else { search.find('.search-box__input').toggleClass('filled', false).text(Lang.translate('search_input') + '...'); } } if (Storage.field('keyboard_type') !== 'lampa') search.find('.search-box__input').hide(); $('body').append(search); $('body').toggleClass('ambience--enable', true); var keyboard = new Keyboard({ layout: 'clarify' }); keyboard.create(); keyboard.listener.follow('change', function (event) { change(event.value); }); keyboard.listener.follow('focus', function (event) { search.toggleClass('search-box--focus', true); }); keyboard.listener.follow('blur', function (event) { search.toggleClass('search-box--focus', false); }); keyboard.listener.follow('back', back); keyboard.listener.follow('enter', enter); keyboard.value(params.input); change(params.input); keyboard.toggle(); } /** * Фильтр * @param {object} params - параметры фильтра * @param {string} params.search - текущий поисковый запрос * @param {object} params.movie - информация о фильме/сериале * @param {string} params.search_one - основной поисковый запрос * @param {string} params.search_two - дополнительный поисковый запрос * @returns {Filter} - экземпляр класса Filter */ function Filter() { var _this2 = this; var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var line = Template.get('filter').addClass('torrent-filter'); var empty = $(''); var data = { sort: [], filter: [] }; var similars = []; var buttons_scroll = new Scroll({ horizontal: true, nopadding: true }); if (typeof params.search == 'string') line.find('.filter--search div').text(Utils$1.shortText(params.search, 20)).removeClass('hide'); function selectSearch() { var _this = this; var search = []; var year = ((params.movie ? params.movie.first_air_date || params.movie.release_date : '0000') + '').slice(0, 4); var earlier = Storage.get('user_clarifys', '{}')[params.movie.id]; search.push({ title: Lang.translate('filter_set_name'), query: '' }); search.push({ title: Lang.translate('global_search'), global_search: true }); if (earlier) { search.push({ title: Lang.translate('search'), separator: true }); earlier.map(function (a) { return a; }).reverse().forEach(function (ear) { search.push({ title: ear, query: ear }); }); } if (params.movie.names && params.movie.names.length) { search.push({ title: Lang.translate('filter_alt_names'), separator: true }); params.movie.names.forEach(function (n) { search.push({ title: n, query: n }); }); } if (params.movie.alternative_titles && params.movie.alternative_titles.titles && params.movie.alternative_titles.titles.length) { params.movie.alternative_titles.titles.forEach(function (a) { if (['us', Storage.field('language')].indexOf(a.iso_3166_1.toLowerCase()) >= 0) { if (!search.find(function (s) { return s.title == a.title; })) { search.push({ title: a.title, query: a.title }); } } }); } search.push({ title: Lang.translate('filter_combinations'), separator: true }); if (similars.length) { similars.forEach(function (sim) { search.push({ title: sim, query: sim }); }); } else { var combinations = []; if (params.search_one) { combinations.push(params.search_one); combinations.push(params.search_one + ' ' + year); if (params.search_two) { combinations.push(params.search_one + ' ' + params.search_two); combinations.push(params.search_one + ' ' + params.search_two + ' ' + year); } } if (params.search_two) { combinations.push(params.search_two); combinations.push(params.search_two + ' ' + year); if (params.search_one) { combinations.push(params.search_two + ' ' + params.search_one); combinations.push(params.search_two + ' ' + params.search_one + ' ' + year); } } combinations.forEach(function (word) { search.push({ title: word, query: word }); }); } var selected; search.forEach(function (elem) { elem.selected = elem.query == params.search && !selected; if (elem.selected) selected = true; }); Select.show({ title: Lang.translate('filter_clarify'), items: search, onBack: this.onBack, onSelect: function onSelect(a) { if (a.global_search) { new SearchInput({ input: params.search || '', onSearch: function onSearch(new_query) { _this.onSearch(new_query, { global: true }); }, onBack: _this.onBack }); return; } if (!a.query) { new SearchInput({ input: params.search, onSearch: function onSearch(new_query) { var earliers = Storage.get('user_clarifys', '{}'); if (!earliers[params.movie.id]) earliers[params.movie.id] = []; if (earliers[params.movie.id].indexOf(new_query) == -1) { earliers[params.movie.id].push(new_query); Storage.set('user_clarifys', earliers); } _this.onSearch(new_query); }, onBack: _this.onBack }); } else { _this.onSearch(a.query); } } }); } empty.on('hover:enter', selectSearch.bind(this)); line.find('.filter--search').on('hover:enter', selectSearch.bind(this)); line.find('.filter--sort').on('hover:enter', function () { _this2.show(Lang.translate('filter_sorted'), 'sort'); }); line.find('.filter--filter').on('hover:enter', function () { _this2.show(Lang.translate('filter_filtred'), 'filter'); }); buttons_scroll.append(line); this.addButtonBack = function () { if (params.movie && params.movie.id) { line.prepend(Template.get('explorer_button_back')); line.find('.filter--back').on('hover:enter', function () { if (Activity.all().length > 1) { Activity.back(); } else { Activity.push({ url: params.movie.url, component: 'full', id: params.movie.id, method: params.movie.name ? 'tv' : 'movie', card: params.movie, source: params.movie.source }); } }); } }; this.show = function (title, type) { var _this3 = this; var where = data[type]; Select.show({ title: title, items: where, onBack: this.onBack, onSelect: function onSelect(a) { if (!a.noselect) _this3.selected(where, a); if (a.items) { Select.show({ title: a.title, items: a.items, onBack: function onBack() { _this3.show(title, type); }, onSelect: function onSelect(b) { if (!b) _this3.selected(a.items, b); _this3.onSelect(type, a, b); _this3.show(title, type); }, onCheck: function onCheck(b) { _this3.onCheck(type, a, b); } }); } else { _this3.onSelect(type, a); } } }); }; this.selected = function (items, a) { items.forEach(function (element) { element.selected = false; }); a.selected = true; }; this.render = function () { return buttons_scroll.render(); }; this.append = function (add) { html.find('.files__body').append(add); }; this.empty = function () { return empty; }; this.toggle = function () { line.find('.filter--sort').toggleClass('selector', data.sort.length ? true : false).toggleClass('hide', data.sort.length ? false : true); line.find('.filter--filter').toggleClass('selector', data.filter.length ? true : false).toggleClass('hide', data.filter.length ? false : true); }; this.set = function (type, items) { data[type] = items; this.toggle(); }; this.get = function (type) { return data[type]; }; this.similar = function (sim) { similars = sim; return empty; }; this.sort = function (items, by) { items.sort(function (c, b) { if (c[by] < b[by]) return 1; if (c[by] > b[by]) return -1; return 0; }); }; this.chosen = function (type, select) { line.find('.filter--' + type + ' > div').html(Utils$1.shortText(select.join(', '), 25)).toggleClass('hide', select.length ? false : true); }; this.destroy = function () { empty.remove(); line.remove(); buttons_scroll.destroy(); empty = null; line = null; data = null; }; } var SERVER = {}; var timers = {}; var callback$1; var callback_back; var autostart_timer; var autostart_progress; var formats = ['asf', 'wmv', 'divx', 'avi', 'mp4', 'm4v', 'mov', '3gp', '3g2', 'mkv', 'trp', 'tp', 'mts', 'mpg', 'mpeg', 'dat', 'vob', 'rm', 'rmvb', 'm2ts', 'ts']; var formats_individual = ['vob', 'm2ts']; function start$5(element, movie) { SERVER.object = element; if (movie) SERVER.movie = movie; if (Platform.is('android') && !Storage.field('internal_torrclient')) { Android.openTorrent(SERVER); if (movie && movie.id) Favorite.add('history', movie, 100); if (callback$1) callback$1(); } else if (Torserver.url()) { loading(); connect(); } else install$1(); } function open$4(hash, movie) { SERVER.hash = hash; if (movie) SERVER.movie = movie; if (Platform.is('android') && !Storage.field('internal_torrclient')) { Android.playHash(SERVER); if (callback$1) callback$1(); } else if (Torserver.url()) { loading(); files(); } else install$1(); } function loading() { Modal.open({ title: '', html: Template.get('modal_loading'), size: 'large', mask: true, onBack: function onBack() { Modal.close(); close$5(); } }); } function connect() { Torserver.connected(function () { hash(); }, function (echo) { Torserver.error(); }); } function hash() { Torserver.hash({ title: SERVER.object.title, link: SERVER.object.MagnetUri || SERVER.object.Link, poster: SERVER.object.poster, data: { lampa: true, movie: SERVER.movie } }, function (json) { SERVER.hash = json.hash; files(); }, function (echo) { //Torserver.error() var jac = Storage.field('parser_torrent_type') == 'jackett'; var tpl = Template.get('torrent_nohash', { title: Lang.translate('title_error'), text: Lang.translate('torrent_parser_no_hash'), url: SERVER.object.MagnetUri || SERVER.object.Link, echo: echo }); if (jac) tpl.find('.is--torlook').remove();else tpl.find('.is--jackett').remove(); Modal.update(tpl); }); } function files() { var repeat = 0; timers.files = setInterval(function () { repeat++; Torserver.files(SERVER.hash, function (json) { if (json.file_stats) { clearInterval(timers.files); if (Storage.field('torrserver_tracktimecode')) { Torserver.viewed(SERVER.hash, function (viewed) { show$3(json.file_stats, viewed || []); }, function () { show$3(json.file_stats, []); }); } else { show$3(json.file_stats); } } }); if (repeat >= 45) { Modal.update(Template.get('error', { title: Lang.translate('title_error'), text: Lang.translate('torrent_parser_timeout') })); Torserver.clear(); Torserver.drop(SERVER.hash); } }, 2000); } function install$1() { Modal.open({ title: '', html: Template.get('torrent_install', {}), size: 'large', onBack: function onBack() { Modal.close(); Controller.toggle('content'); } }); } function show$3(files, viewed) { files.sort(function (a, b) { var an = a.path.replace(/\d+/g, function (m) { return m.length > 3 ? m : ('000' + m).substr(-4); }); var bn = b.path.replace(/\d+/g, function (m) { return m.length > 3 ? m : ('000' + m).substr(-4); }); return an.localeCompare(bn); }); var active = Activity.active(), movie = active.movie || SERVER.movie || {}; var plays = Torserver.clearFileName(files.filter(function (a) { var exe = a.path.split('.').pop().toLowerCase(); return formats.indexOf(exe) >= 0; })); var seasons = []; plays.forEach(function (element) { var info = Torserver.parse({ movie: movie, files: plays, filename: element.path_human, path: element.path }); if (info.serial && info.season && seasons.indexOf(info.season) == -1) { seasons.push(info.season); } }); if (seasons.length) { Api.seasons(movie, seasons, function (data) { list$1(plays, { movie: movie, seasons: data, files: files, viewed: viewed || [] }); }); } else { list$1(plays, { movie: movie, files: files, viewed: viewed || [] }); } } function parseSubs(path, files) { var name = path.split('/').pop().split('.').slice(0, -1).join('.'); var index = -1; var supportedFormats = subsrt.list(); var subtitles = files.filter(function (a) { var _short = a.path.split('/').pop(); var issub = supportedFormats.indexOf(a.path.split('.').pop().toLowerCase()) >= 0; return _short.indexOf(name) >= 0 && issub; }).map(function (a) { index++; var segments = a.path.split('/'); segments.pop(); // drop filename var label = segments.slice(1).join(' - '); // drop initial folder and concat the rest return { label: label, url: Torserver.stream(a.path, SERVER.hash, a.id).replace('&preload', '&play'), index: index }; }); return subtitles.length ? subtitles : false; } function preload(data, run) { var has_server = Torserver.ip() && data.url.indexOf(Torserver.ip()) > -1; var has_preload = data.url.indexOf('&preload') > -1; var need_preload = has_server && has_preload; if (need_preload) { var checkout; var network = new Request(); var first = true; var stopped = false; var last_bytes = 0; var last_change = Date.now(); var stop = function stop() { stopped = true; clearTimeout(checkout); network.clear(); Loading$1.stop(); }; Loading$1.start(stop, '', { media: data }); var next = function next() { if (stopped) return; if (Date.now() - last_change > 30000) { stop(); } else checkout = setTimeout(update, 1000); }; var update = function update() { network.timeout(2000); network.silent(first ? data.url : data.url.replace('&preload', '&stat'), function (res) { if (stopped) return; var pb = res.preloaded_bytes || 0, ps = res.preload_size || 0, sp = res.download_speed ? Utils$1.bytesToSize(res.download_speed * 8, true) : '0.0', active_peers = parseInt(res.active_peers || 0), total_peers = parseInt(res.total_peers || 0); var progress = Math.min(100, pb * 100 / ps); if (progress >= 95 || isNaN(progress)) { stop(); run(); } else { if (pb > last_bytes) { last_bytes = pb; last_change = Date.now(); } Loading$1.setProgress(progress, { speed: sp, active_peers: active_peers, total_peers: total_peers }); next(); } }, next); first = false; }; update(); } else run(); } function list$1(items, params) { var html = $('
    '); var playlist = []; var scroll_to_element; var first_item; Lampa.Listener.send('torrent_file', { type: 'list_open', items: items, params: params }); var folder = ''; items.forEach(function (element, inx) { var exe = element.path.split('.').pop().toLowerCase(); var info = Torserver.parse({ movie: params.movie, files: items, filename: element.path_human, path: element.path, is_file: formats_individual.indexOf(exe) >= 0 }); var view = Timeline$1.view(info.hash); var serverView = (params.viewed || []).filter(function (v) { return v.file_index == element.id; })[0]; if (params.movie && params.movie.runtime) { view.duration = params.movie.runtime * 60; } if (Storage.field('torrserver_tracktimecode') && serverView && serverView.timecode > 0) { view.time = serverView.timecode; if (view.duration > 0) { view.percent = Math.min(100, Math.round(view.time / view.duration * 100)); } Timeline$1.update(view); } var item; Arrays.extend(element, { season: info.season, episode: info.episode, title: element.path_human, first_title: params.movie.name || params.movie.title, card: params.movie, size: Utils$1.bytesToSize(element.length), url: Torserver.stream(element.path, SERVER.hash, element.id), torrent_hash: SERVER.hash, ffprobe: SERVER.object && SERVER.object.ffprobe ? SERVER.object.ffprobe : false, timeline: view, air_date: '--', img: './img/img_broken.svg', exe: exe, viewed: function viewed(time) { if (Storage.field('torrserver_tracktimecode')) { Torserver.viewedSet(SERVER.hash, element.id, time); } } }); if (params.seasons) { var episodes = params.seasons[info.season]; element.title = (info.episode ? info.episode + ' / ' : '') + element.path_human; element.fname = element.title; if (episodes) { var episode = episodes.episodes.filter(function (a) { return a.episode_number == info.episode; })[0]; if (episode) { if (episode.runtime) { view.duration = episode.runtime * 60; } if (Storage.field('torrserver_tracktimecode') && serverView && serverView.timecode > 0) { view.time = serverView.timecode; if (view.duration > 0) { view.percent = Math.min(100, Math.round(view.time / view.duration * 100)); } Timeline$1.update(view); } element.title = info.episode + ' / ' + episode.name; element.air_date = Utils$1.parseTime(episode.air_date).full; element.fname = episode.name; if (episode.still_path) element.img = Api.img(episode.still_path);else if (episode.img) element.img = episode.img; } } if (info.episode) { item = Template.get('torrent_file_serial', element); item.find('.torrent-serial__content').append(Timeline$1.render(view)); } else { item = Template.get('torrent_file', element); item.append(Timeline$1.render(view)); } } else if (items.length == 1 && params.movie && !params.movie.name) { element.fname = params.movie.title || element.title; if (params.movie.backdrop_path) element.img = Api.img(params.movie.backdrop_path); item = Template.get('torrent_file_serial', element); item.find('.torrent-serial__line').empty().text(params.movie.tagline || ''); item.find('.torrent-serial__episode').remove(); item.find('.torrent-serial__content').append(Timeline$1.render(view)); } else { item = Template.get('torrent_file', element); item.append(Timeline$1.render(view)); if (params.movie.title) element.title = params.movie.title; } if (element.img !== './img/img_broken.svg') element.thumbnail = element.img; item[0].visibility = 'hidden'; if (view.percent > 0) scroll_to_element = item; element.subtitles = parseSubs(element.path, params.files); element.title = (element.fname || element.title).replace(/<[^>]*>?/gm, ''); element.subtitle = element.episode ? Lang.translate('torrent_serial_episode') + ': ' + element.episode : ''; // копируем объект, чтобы не было ссылок на один и тот же объект в плейлисте var playlist_element = {}; for (var a in element) { playlist_element[a] = element[a]; } playlist.push(playlist_element); element.playlist = playlist; item.on('hover:enter', function () { stopAutostart(); // если это андроид, но не андроид, то нефиг смотреть if (navigator.userAgent.toLowerCase().indexOf('android') >= 0 && !Platform.is('android')) return Platform.install('apk'); if (params.movie.id) Favorite.add('history', params.movie, 100); preload(element, function () { Player.play(element); Player.callback(function () { Controller.toggle('modal'); }); Player.playlist(playlist); if (callback$1) { callback$1(); callback$1 = false; } Lampa.Listener.send('torrent_file', { type: 'onenter', element: element, item: item, items: items, params: params }); }); }).on('hover:long', function () { stopAutostart(); var enabled = Controller.enabled().name; var menu = [{ title: Lang.translate('time_reset'), timeclear: true }, { title: Lang.translate('time_viewed'), timefull: true }]; if (Platform.is('webos')) { menu.push({ title: Lang.translate('player_lauch') + ' - WebOS', player: 'webos' }); } if (Platform.is('android')) { menu.push({ title: Lang.translate('player_lauch') + ' - Android', player: 'android' }); } menu.push({ title: Lang.translate('player_lauch') + ' - Lampa', player: 'lampa' }); menu.push({ title: Lang.translate('copy_link'), link: true }); Lampa.Listener.send('torrent_file', { type: 'onlong', element: element, item: item, menu: menu, items: items, params: params }); Select.show({ title: Lang.translate('title_action'), items: menu, onBack: function onBack() { Controller.toggle(enabled); }, onSelect: function onSelect(a) { if (a.timeclear) { view.percent = 0; view.time = 0; view.duration = 0; element.timeline = view; Timeline$1.update(view); if (Storage.field('torrserver_tracktimecode')) { Torserver.viewedSet(SERVER.hash, element.id, 0); } } if (a.timefull) { view.percent = 100; view.time = view.duration; element.timeline = view; Timeline$1.update(view); } if (a.link) { Utils$1.copyTextToClipboard(element.url.replace('&preload', '&play'), function () { Noty.show(Lang.translate('copy_secuses')); }, function () { Noty.show(Lang.translate('copy_error')); }); } Controller.toggle(enabled); if (a.player) { Player.runas(a.player); item.trigger('hover:enter'); } } }); }).on('hover:focus', function () { Lampa.Listener.send('torrent_file', { type: 'onfocus', element: element, item: item, items: items, params: params }); }).on('visible', function () { var img = item.find('img'); img[0].onload = function () { img.addClass('loaded'); }; img[0].src = img.attr('data-src'); }); if (element.folder_name && element.folder_name !== folder) { html.append($('
    ' + element.folder_name + '
    ')); folder = element.folder_name; } html.append(item); if (!first_item) first_item = item; Lampa.Listener.send('torrent_file', { type: 'render', element: element, item: item, items: items, params: params }); }); if (items.length == 0) html = Template.get('error', { title: Lang.translate('empty_title'), text: Lang.translate('torrent_parser_nofiles') });else Modal.title(Lang.translate('title_files')); if (playlist.length == 1) autostart(first_item); Modal.update(html); if (scroll_to_element) Controller.collectionFocus(scroll_to_element, Modal.scroll().render()); } function autostart(item) { var tim = Date.now(); var div = $('
    '); autostart_timer = setInterval(function () { var dif = (Date.now() - tim) / 1000; div.css('height', Math.round(dif / 10 * 100) + '%'); if (dif > 10) { stopAutostart(); item.trigger('hover:enter'); } }, 10); Keypad.listener.follow('keydown', listenKeydown); autostart_progress = div; item.prepend(div); } function listenKeydown() { stopAutostart(); Keypad.listener.remove('keydown', listenKeydown); } function stopAutostart() { clearInterval(autostart_timer); if (autostart_progress) autostart_progress.remove(); autostart_progress = null; } function opened(call) { callback$1 = call; } function back$4(call) { callback_back = call; } function close$5() { Torserver.drop(SERVER.hash); Torserver.clear(); clearInterval(timers.files); if (callback_back) { callback_back(); } else { Controller.toggle('content'); } callback_back = false; SERVER = {}; clearInterval(autostart_timer); Lampa.Listener.send('torrent_file', { type: 'list_close' }); } var Torrent = { start: start$5, open: open$4, opened: opened, back: back$4 }; function Results(source) { var timer, html = Template.elem('div'), items = [], active = 0, query; var source_name = 'search_' + source.title.toLowerCase(); this.listener = Subscribe$2(); this.params = source.params; this.create = function () { this.empty(); }; this.recall = function (last_query) { var _this = this; Cache.getData('other', source_name + '_' + (last_query || 'last'), 60 * 24).then(function (data) { if (!data) return; _this.clear(); html.empty(); source.onRecall && source.onRecall(data, last_query); data.forEach(_this.build.bind(_this)); _this.listener.send('finded', { count: _this.count(data), data: data }); })["catch"](function () {}); }; this.empty = function () { html.empty().append($('
    ' + Lang.translate(query ? source.params.nofound || 'search_nofound' : source.params.start_typing || 'search_start_typing') + '
    ')); }; this.loading = function () { this.listener.send('start'); html.empty().append($('
    ' + Lang.translate('search_searching') + '
    ')); }; this.cancel = function () { clearTimeout(timer); if (source.onCancel) source.onCancel(); }; this.search = function (value, immediately) { var _this2 = this; clearTimeout(timer); if (value.length >= 3) { timer = setTimeout(function () { if (query == value) return; query = value; _this2.loading(); source.search({ query: encodeURIComponent(value) }, function (data) { _this2.clear(); _this2.dmca(data); var count = _this2.count(data); if (count > 0) { html.empty(); var copy = Arrays.clone(data); Cache.rewriteData('other', source_name + '_' + value, copy)["catch"](function () {}); Cache.rewriteData('other', source_name + '_last', copy)["catch"](function () {}); data.forEach(_this2.build.bind(_this2)); } else _this2.empty(); _this2.listener.send('finded', { count: count, data: data }); }); }, immediately ? 10 : 2500); } else { query = value; this.clear(); if (!value) this.recall(''); } }; this.count = function (result) { var count = 0; result.forEach(function (data) { count += data.results.length; }); return count; }; this.dmca = function (result) { if (Arrays.isArray(window.lampa_settings.dcma)) { result.forEach(function (data) { data.results = data.results.filter(function (item) { return !window.lampa_settings.dcma.find(function (b) { return b.id == item.id && b.cat == (item.name ? 'tv' : 'movie'); }); }); }); } }; this.build = function (data) { var _this3 = this; data.noimage = true; source.params.card_view = 6; if (Arrays.isArray(window.lampa_settings.dcma)) { data.results = data.results.filter(function (item) { return !window.lampa_settings.dcma.find(function (b) { return b.id == item.id && b.cat == (item.name ? 'tv' : 'movie'); }); }); } var line = Utils$1.createInstance(Line$4, data, _objectSpread2(_objectSpread2({}, source.params), {}, { module: Helper$b.only('Items', 'Create', 'More') })); line.use({ onDown: this.down.bind(this), onUp: this.up.bind(this), onBack: this.back.bind(this), onMore: function onMore() { if (source.onMore) source.onMore({ data: data, line: line, query: query }, function () { _this3.listener.send('select'); }); }, onInstance: function onInstance(item, item_data) { item.use({ onEnter: function onEnter() { if (source.onSelect) { source.onSelect({ data: data, line: line, query: query, element: item_data, item_data: item_data }, function () { _this3.listener.send('select'); }); } else { _this3.listener.send('select'); router.call('full', item_data); } } }); } }); if (source.onRender) source.onRender(line); line.create(); items.push(line); html.append(line.render(true)); }; this.any = function () { return items.length; }; this.back = function () { this.listener.send('back'); }; this.down = function () { active++; active = Math.min(active, items.length - 1); items[active].toggle(); this.listener.send('toggle', { element: items[active].render() }); }; this.up = function () { active--; if (active < 0) this.listener.send('up'); if (active < 0) { active = 0; } else { items[active].toggle(); this.listener.send('toggle', { element: items[active].render() }); } }; this.clear = function () { this.empty(); active = 0; Arrays.destroy(items); items = []; this.listener.send('clear'); }; this.toggle = function () { var _this4 = this; Controller.add('search_results', { invisible: true, toggle: function toggle() { Controller.collectionSet(html); if (items.length) { items[active].toggle(); _this4.listener.send('toggle', { element: items[active].render() }); } }, back: function back() { _this4.listener.send('back'); } }); Controller.toggle('search_results'); }; this.render = function (js) { return js ? html : $(html); }; this.destroy = function () { clearTimeout(timer); this.clear(); this.listener.destroy(); }; } var stop_keys = ['пор', 'порн', 'порно', 'секс', 'член', 'гей', 'лгбт', 'лесби', 'бисексуал', 'трансгендер', 'por', 'porn', 'porno', 'sex', 'hot', 'xxx', 'lgbt', 'lesbian', 'bisexual', 'queer', 'transgender', 'same sex']; function Sources() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var scroll, last, active, last_query = ''; var html = document.createElement('div'), results = []; this.listener = Subscribe$2(); this.create = function () { scroll = new Scroll({ over: true, mask: false, horizontal: true }); var sources; if (permit.child) { sources = [Api.sources.cub.discovery()]; sources.forEach(this.build.bind(this)); } else { sources = params.sources || Api.availableDiscovery(); sources.forEach(this.build.bind(this)); if (!params.sources) { params.additional.forEach(this.build.bind(this)); } } this.enable(results[0]); if (results.length < 2) { scroll.render(true).addClass('hide'); html.addClass('search__results-offset'); } // results.forEach(source=>{ // source.recall(last_query) // }) }; this.enable = function (result) { active = result; if (active.params.lazy && last_query) active.search(last_query, true);else { active.recall(last_query); } html.empty().append(result.render(true)); scroll.render().find('.search-source').removeClass('active').eq(results.indexOf(result)).addClass('active'); Layer.visible(result.render(true)); }; this.build = function (source) { var _this = this; var tab = $('
    ' + source.title + '
    0
    '); var result = new Results(source); result.create(); if (source.params.lazy) tab.find('.search-source__count').remove(); result.listener.follow('start', function () { tab.addClass('search-source--loading'); tab.find('.search-source__count').html(' '); }); result.listener.follow('clear', function () { tab.find('.search-source__count').text(0); }); result.listener.follow('finded', function (e) { tab.removeClass('search-source--loading'); tab.find('.search-source__count').text(e.count); if (active == result) Layer.visible(result.render(true)); _this.listener.send('finded', { source: source, result: result, count: e.count, data: e.data }); }); result.listener.follow('up', function (e) { if (results.length < 2) _this.listener.send('up');else _this.toggle(); }); result.listener.follow('select', this.listener.send.bind(this.listener, 'select')); result.listener.follow('back', this.listener.send.bind(this.listener, 'back')); result.listener.follow('toggle', function (e) { _this.listener.send('toggle', { source: source, result: e.result, element: e.element }); }); tab.on('hover:enter', function () { _this.enable(result); }).on('hover:focus', function (e) { last = e.target; scroll.update($(e.target)); }); scroll.append(tab); results.push(result); this.listener.send('create', { source: source, result: result }); }; this.toggle = function (from_search) { var _this2 = this; Controller.add('search_sources', { toggle: function toggle() { Controller.collectionSet(scroll.render(true)); Controller.collectionFocus(last, scroll.render(true)); if (from_search && results.length < 2 && active.any()) active.toggle(); }, update: function update() {}, up: function up() { _this2.listener.send('up'); }, down: function down() { if (active.any()) active.toggle(); }, right: function right() { Navigator.move('right'); }, left: function left() { Navigator.move('left'); }, back: function back() { _this2.listener.send('back'); } }); Controller.toggle('search_sources'); }; this.search = function (query, immediately) { results.forEach(function (result) { return result.cancel(); }); if (!stop_keys.find(function (k) { return k == query.toLowerCase().trim(); })) { last_query = query; this.listener.send('search', { query: query, immediately: immediately }); results.forEach(function (result) { if (!result.params.lazy || active === result) result.search(query, immediately); }); } }; this.cancel = function () { results.forEach(function (result) { return result.cancel(); }); }; this.tabs = function () { return scroll.render(true); }; this.render = function (js) { return js ? html : $(html); }; this.destroy = function () { scroll.destroy(); results.forEach(function (result) { return result.cancel(); }); this.listener.destroy(); }; } function History() { var scroll, last, keys = []; this.listener = Subscribe$2(); this.create = function () { var _this = this; scroll = new Scroll({ over: true, mask: false, horizontal: true }); scroll.onWheel = function (step) { _this.toggle(); Controller.enabled().controller[step > 0 ? 'right' : 'left'](); }; scroll.onScroll = function (step) {}; keys = Storage.get('search_history', '[]'); keys.map(function (v) { return v; }).reverse().slice(0, 15).forEach(function (key) { _this.append(key); }); if (!keys.length) scroll.append($('
    ' + Lang.translate('search_empty') + '
    ')); }; this.append = function (value) { var _this2 = this; var key = $('
    ' + value + '
    '); key.on('hover:enter', function () { _this2.listener.send('enter', { value: value }); }).on('hover:focus', function (e) { last = e.target; scroll.update($(e.target), true); }).on('hover:long', function () { var selc = scroll.render().find('.selector'); Arrays.remove(keys, value); Storage.set('search_history', keys); Storage.remove('search_history', value); var index = selc.index(key); if (index > 0) last = selc.eq(index - 1)[0];else if (selc[index + 1]) last = selc.eq(index + 1)[0]; key.remove(); if (selc.length - 1 <= 0) last = false; Controller.collectionFocus(last, scroll.render()); }); scroll.append(key); }; this.add = function (value) { var inx = keys.indexOf(value); if (inx == -1) { keys.push(value); } else { Arrays.remove(keys, value); keys.push(value); } Storage.set('search_history', keys); }; this.toggle = function () { var _this3 = this; Controller.add('search_history', { toggle: function toggle() { Controller.collectionSet(scroll.render()); Controller.collectionFocus(last, scroll.render()); }, update: function update() {}, up: function up() { _this3.listener.send('up'); }, down: function down() { _this3.listener.send('down'); }, right: function right() { Navigator.move('right'); }, back: function back() { _this3.listener.send('back'); }, left: function left() { Navigator.move('left'); } }); Controller.toggle('search_history'); }; this.any = function () { return keys.length; }; this.render = function () { return scroll.render(); }; this.destroy = function () { scroll.destroy(); this.listener.destroy(); keys = null; last = null; }; } var html$9 = Template.elem('div', { "class": 'main-search' }), search$1, history, sources, keyboard$1, scroll, input$1 = '', params$1 = {}, additional = [], listener$4 = Subscribe$2(); function init$y() { Head.addIcon(Template.string('icon_search'), open$3).addClass('open--search'); } function open$3() { var use_params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; params$1 = use_params; input$1 = use_params.input || ''; $('body').toggleClass('ambience--enable', true); $('body').toggleClass('search--open', true); create$6(); toggle$3(); Layer.update(html$9); listener$4.send('open'); } function toggle$3() { Controller.add('search', { invisible: true, toggle: function toggle() { keyboard$1.toggle(); }, update: function update() {}, back: destroy$2 }); Controller.toggle('search'); } function scrollTo(element) { scroll.update(element ? element : search$1.find('.search__input'), true); } function create$6() { search$1 = Template.get('search'); scroll = new Scroll({ step: 300 }); scroll.height(); scroll.render().addClass('search'); scroll.append(HeadBackward(Lang.translate('search'))); scroll.append(search$1); html$9.append(scroll.render(true)); scroll.onScroll = function (step) { Layer.visible(scroll.render(true)); }; if (Storage.field('keyboard_type') !== 'lampa') search$1.find('.search__input').hide(); createKeyboard(); createHistory(); createSources(); keyboard$1.value(input$1); } function createSources() { sources = new Sources({ sources: params$1.sources, additional: additional }); listener$4.send('sources', { sources: sources }); sources.create(); sources.listener.follow('back', destroy$2); sources.listener.follow('up', function () { if (history.any()) history.toggle();else keyboard$1.toggle(); scrollTo(); }); sources.listener.follow('toggle', function (e) { scrollTo(e.element); }); sources.listener.follow('select', function (e) { if (input$1) history.add(input$1); destroy$2(); }); search$1.find('.search__sources').append(sources.tabs()); search$1.find('.search__results').append(sources.render()); } function createHistory() { history = new History(); history.create(); history.listener.follow('down', function () { sources.toggle(true); }); history.listener.follow('up', function () { keyboard$1.toggle(); }); history.listener.follow('enter', function (event) { keyboard$1.value(event.value); sources.search(event.value, true); }); history.listener.follow('back', destroy$2); search$1.find('.search__history').append(history.render()); } function createKeyboard() { keyboard$1 = new Keyboard({ layout: 'search' }); keyboard$1.create(); keyboard$1.listener.follow('change', function (event) { input$1 = event.value; if (input$1) { search$1.find('.search__input').toggleClass('filled', true).html(input$1.replace(/\s/g, ' ')); sources.search(input$1); } else { search$1.find('.search__input').toggleClass('filled', false).text(Lang.translate('search_input') + '...'); sources.search(''); } }); keyboard$1.listener.follow('down', function () { if (history.any()) history.toggle();else sources.toggle(); }); keyboard$1.listener.follow('hover', function () { input$1.length <= 2 ? sources.cancel() : sources.search(input$1); }); keyboard$1.listener.follow('back', destroy$2); } function addSource(source) { additional.push(source); } function removeSource(source) { Arrays.remove(additional, source); } function render$3(js) { return js ? html$9 : $(html$9); } function destroy$2() { keyboard$1.destroy(); history.destroy(); sources.destroy(); search$1.remove(); html$9.empty(); $('body').toggleClass('ambience--enable', false); $('body').toggleClass('search--open', false); if (params$1.onBack) params$1.onBack();else Controller.toggle('content'); params$1 = {}; input$1 = ''; listener$4.send('close'); } function close$4() { destroy$2(); } var Search = { init: init$y, listener: listener$4, open: open$3, render: render$3, addSource: addSource, removeSource: removeSource, close: close$4 }; var CardParser = /*#__PURE__*/function (_Constructor) { _inherits(CardParser, _Constructor); var _super = _createSuper(CardParser); function CardParser(data) { var _this; _classCallCheck(this, CardParser); _this = _super.call(this, data); _this.use(Callback$1); return _this; } _createClass(CardParser, [{ key: "create", value: function create() { this.html = Template.js('card_parser', this.data); this.html.on('visible', this.emit.bind(this, 'visible')); this.emit('create'); } }]); return CardParser; }(Constructor({})); var network$2 = new Request(); var TMDB_GENRE_ANIMATION = 16; var ANIME_LANGUAGES = ['ja', 'zh']; function isAnimeMovie(movie) { return ANIME_LANGUAGES.includes(movie.original_language) && (movie.genres || []).some(function (g) { return g.id == TMDB_GENRE_ANIMATION; }); } function init$x() { Storage.set('parser_torrent_type', Storage.get('parser_torrent_type') || 'jackett'); var source = { title: Lang.translate('title_parser'), search: function search(params, oncomplite) { get$5({ search: decodeURIComponent(params.query), other: true, from_search: true }, function (json) { json.Results.sort(function (a, b) { return (b.Seeders || 0) - (a.Seeders || 0); }); json.title = Lang.translate('title_parser'); json.results = json.Results.slice(0, 20); json.total = json.Results.length; json.total_pages = Math.ceil(json.total / 20); json.Results = null; json.results.forEach(function (element) { element.Title = Utils$1.shortText(element.Title, 110); element.params = { createInstance: function createInstance(item) { return new CardParser(item); } }; }); oncomplite(json.results.length ? [json] : []); }, function () { oncomplite([]); }); }, onRecall: function onRecall(data, last_query) { data[0].results.forEach(function (element) { element.params = { createInstance: function createInstance(item) { return new CardParser(item); } }; }); }, onCancel: network$2.clear.bind(network$2), params: { lazy: true }, onMore: function onMore(params, close) { close(); Activity.push({ url: '', title: Lang.translate('title_torrents'), component: 'torrents', search: params.query, from_search: true, page: 1 }); }, onSelect: function onSelect(params, close) { Torrent.start(params.element, { title: params.element.Title }); Torrent.back(params.line.toggle.bind(params.line)); } }; function addSource() { var reg = Platform.is('android') ? true : Torserver.url(); if (Storage.field('parse_in_search') && reg) Search.addSource(source); } Storage.listener.follow('change', function (e) { if (e.name == 'parse_in_search' || e.name == 'torrserver_url' || e.name == 'torrserver_url_two' || e.name == 'torrserver_use_link') { Search.removeSource(source); addSource(); } }); addSource(); } function parserLinks(type) { if (type == 'jackett') { return [{ url: Storage.field('jackett_url').replace('jacred.xyz', 'jac.red'), key: Storage.field('jackett_key'), rank: 0 }, { url: Storage.field('jackett_url_two').replace('jacred.xyz', 'jac.red'), key: Storage.field('jackett_key_two'), rank: 1 }]; } else if (type == 'prowlarr') { return [{ url: Storage.field('prowlarr_url'), key: Storage.field('prowlarr_key'), rank: 0 }, { url: Storage.field('prowlarr_url_two'), key: Storage.field('prowlarr_key_two'), rank: 1 }]; } else return []; } function selectParserLinks(type) { var use = Storage.field('parser_use_link') || 'one'; var links = parserLinks(type); if (use == 'one') return links[0] && links[0].url ? [links[0]] : []; if (use == 'two') return links[1] && links[1].url ? [links[1]] : []; return links.filter(function (l) { return l && l.url; }); } function resultKey(item) { if (item.MagnetUri) return 'm:' + item.MagnetUri; if (item.downloadUrl) return 'm:' + item.downloadUrl; var size = item.Size || item.size || ''; var tracker = item.Tracker || ''; var hash = item.hash || Utils$1.hash((item.Title || '') + size + tracker); return 'h:' + hash + '|' + size + '|' + tracker; } function mergeResults(items) { var map = new Map(); items.forEach(function (item) { var key = resultKey(item); var prev = map.get(key); if (!prev) map.set(key, item);else { var prev_rank = typeof prev.source_rank == 'number' ? prev.source_rank : 0; var next_rank = typeof item.source_rank == 'number' ? item.source_rank : 0; if (next_rank < prev_rank) { map.set(key, item); } else if (next_rank == prev_rank) { var prev_time = prev.checked_at || 0; var next_time = item.checked_at || 0; if (next_time > prev_time) map.set(key, item); } } }); return Array.from(map.values()); } function get$5() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oncomplite = arguments.length > 1 ? arguments[1] : undefined; var onerror = arguments.length > 2 ? arguments[2] : undefined; function complite(data) { oncomplite(data); } function error(e) { onerror(e); } if (Storage.field('parser_torrent_type') == 'jackett') { var links = selectParserLinks('jackett'); if (links.length) { var calls = links.map(function (link) { return function (done, fail) { var base = Utils$1.checkEmptyUrl(link.url); jackett(params, base, link.key, link.rank, done, fail); }; }); if (calls.length == 1) calls[0](complite, error);else { var results = []; var pending = calls.length; var success = false; var first_error; calls.forEach(function (call, index) { call(function (json) { results[index] = json; success = true; if (--pending == 0) { var _ref; var merged = mergeResults((_ref = []).concat.apply(_ref, _toConsumableArray(results.map(function (r) { return r && r.Results ? r.Results : []; })))); complite({ Results: merged }); } }, function (e) { results[index] = null; first_error = first_error || e; if (--pending == 0) { if (success) { var _ref2; var merged = mergeResults((_ref2 = []).concat.apply(_ref2, _toConsumableArray(results.map(function (r) { return r && r.Results ? r.Results : []; })))); complite({ Results: merged }); } else error(first_error || ''); } }); }); } } else { error(Lang.translate('torrent_parser_set_link') + ': Jackett'); } } else if (Storage.field('parser_torrent_type') == 'prowlarr') { var _links = selectParserLinks('prowlarr'); if (_links.length) { var _calls = _links.map(function (link) { return function (done, fail) { var base = Utils$1.checkEmptyUrl(link.url); prowlarr(params, base, link.key, link.rank, done, fail); }; }); if (_calls.length == 1) _calls[0](complite, error);else { var _results = []; var _pending = _calls.length; var _success = false; var _first_error; _calls.forEach(function (call, index) { call(function (json) { _results[index] = json; _success = true; if (--_pending == 0) { var _ref3; var merged = mergeResults((_ref3 = []).concat.apply(_ref3, _toConsumableArray(_results.map(function (r) { return r && r.Results ? r.Results : []; })))); complite({ Results: merged }); } }, function (e) { _results[index] = null; _first_error = _first_error || e; if (--_pending == 0) { if (_success) { var _ref4; var merged = mergeResults((_ref4 = []).concat.apply(_ref4, _toConsumableArray(_results.map(function (r) { return r && r.Results ? r.Results : []; })))); complite({ Results: merged }); } else error(_first_error || ''); } }); }); } } else { error(Lang.translate('torrent_parser_set_link') + ': Prowlarr'); } } else if (Storage.field('parser_torrent_type') == 'torrserver') { var torr_link = Storage.field(Storage.field('torrserver_use_link') == 'two' ? 'torrserver_url_two' : 'torrserver_url'); if (torr_link) { torrserver(params, Utils$1.checkEmptyUrl(torr_link), complite, error); } else { error(Lang.translate('torrent_parser_set_link') + ': TorrServer'); } } } function viewed(hash) { var view = Storage.cache('torrents_view', 5000, []); return view.indexOf(hash) > -1; } function jackett() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var base_url = arguments.length > 1 ? arguments[1] : undefined; var api_key = arguments.length > 2 ? arguments[2] : undefined; var source_rank = arguments.length > 3 ? arguments[3] : undefined; var oncomplite = arguments.length > 4 ? arguments[4] : undefined; var onerror = arguments.length > 5 ? arguments[5] : undefined; network$2.timeout(1000 * Storage.field('parse_timeout')); var u = base_url + '/api/v2.0/indexers/' + (Storage.field('jackett_interview') == 'healthy' ? 'status:healthy' : 'all') + '/results?apikey=' + (api_key || '') + '&Query=' + encodeURIComponent(params.search); if (!params.from_search) { var genres = params.movie.genres.map(function (a) { return a.name; }); if (!params.clarification) { u = Utils$1.addUrlComponent(u, 'title=' + encodeURIComponent(params.movie.title)); u = Utils$1.addUrlComponent(u, 'title_original=' + encodeURIComponent(params.movie.original_title)); } u = Utils$1.addUrlComponent(u, 'year=' + encodeURIComponent(((params.movie.first_air_date || params.movie.release_date || '0000') + '').slice(0, 4))); u = Utils$1.addUrlComponent(u, 'is_serial=' + (params.movie.original_name ? '2' : params.other ? '0' : '1')); u = Utils$1.addUrlComponent(u, 'genres=' + encodeURIComponent(genres.join(','))); if (!params.global) u = Utils$1.addUrlComponent(u, 'Category[]=' + (params.movie.number_of_seasons > 0 ? 5000 : 2000) + (isAnimeMovie(params.movie) ? ',5070' : '')); } network$2["native"](u, function (json) { if (json.Results) { var checked_at = Date.now(); json.Results.forEach(function (element) { element.PublisTime = Utils$1.strToTime(element.PublishDate); element.hash = Utils$1.hash(element.Title); element.viewed = viewed(element.hash); element.size = Utils$1.bytesToSize(element.Size); element.checked_at = checked_at; element.source_rank = source_rank; }); oncomplite(json); } else onerror(Lang.translate('torrent_parser_no_responce') + ' (' + base_url + ')'); }, function (a, c) { onerror(Lang.translate('torrent_parser_no_responce') + ' (' + base_url + ')'); }); } // доки https://wiki.servarr.com/en/prowlarr/search#search-feed function prowlarr() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var base_url = arguments.length > 1 ? arguments[1] : undefined; var api_key = arguments.length > 2 ? arguments[2] : undefined; var source_rank = arguments.length > 3 ? arguments[3] : undefined; var oncomplite = arguments.length > 4 ? arguments[4] : undefined; var onerror = arguments.length > 5 ? arguments[5] : undefined; var q = []; q.push({ name: 'apikey', value: api_key || '' }); q.push({ name: 'query', value: params.search }); if (!params.from_search) { var isSerial = !!params.movie.original_name; if (!params.global) { var baseCategory = params.movie.number_of_seasons > 0 ? '5000' : '2000'; q.push({ name: 'categories', value: baseCategory }); if (isAnimeMovie(params.movie)) q.push({ name: 'categories', value: '5070' }); } q.push({ name: 'type', value: isSerial ? 'tvsearch' : 'search' }); } var u = Utils$1.buildUrl(base_url, '/api/v1/search', q); network$2.timeout(1000 * Storage.field('parse_timeout')); network$2["native"](u, function (json) { if (Array.isArray(json)) { var checked_at = Date.now(); oncomplite({ Results: json.filter(function (e) { return e.protocol === 'torrent'; }).map(function (e) { var hash = Utils$1.hash(e.title); var timeValue = Utils$1.strToTime(e.publishDate); return { Title: e.title, Tracker: e.indexer, Size: e.size, PublishDate: Utils$1.strToTime(e.publishDate), PublisTime: timeValue, Seeders: parseInt(e.seeders), Peers: parseInt(e.leechers), MagnetUri: e.downloadUrl, viewed: viewed(hash), checked_at: checked_at, source_rank: source_rank, hash: hash }; }) }); } else { onerror(Lang.translate('torrent_parser_request_error') + ' (' + JSON.stringify(json) + ')'); } }, function () { onerror(Lang.translate('torrent_parser_no_responce') + ' (' + base_url + ')'); }); } // TorrServer exposes two search backends: // /search/?query=... — built-in rutor scraper (no setup). // /torznab/search/?query=... — Torznab indexer(s) configured inside TorrServer // (EnableTorznabSearch + TorznabUrls). Same JSON // shape (models.TorrentDetails) as /search/. // `torrserver_search_type`: 'rutor' | 'torznab' | 'both' (default 'both'). function normalizeTorrServerSize(size) { if (!size) return ''; return String(size).replace(/\s*GCiB\b/gi, ' GB').replace(/\s*MCiB\b/gi, ' MB').replace(/\s*KCiB\b/gi, ' KB'); } function torrserver() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var base_url = arguments.length > 1 ? arguments[1] : undefined; var oncomplite = arguments.length > 2 ? arguments[2] : undefined; var onerror = arguments.length > 3 ? arguments[3] : undefined; network$2.timeout(1000 * Storage.field('parse_timeout')); var mode = Storage.field('torrserver_search_type') || 'both'; function mapResult(e, source_rank) { var hash = Utils$1.hash(e.Title); var timeValue = Utils$1.strToTime(e.CreateDate); // Some torznab indexers expose only a .torrent download URL via `Link` // and no magnet. If the link itself is a magnet URI, fall back to it. var magnet = e.Magnet || (/^magnet:/i.test(e.Link || '') ? e.Link : ''); return { Title: e.Title, Tracker: e.Tracker, size: normalizeTorrServerSize(e.Size), Size: Utils$1.sizeToBytes(normalizeTorrServerSize(e.Size)), PublishDate: Utils$1.strToTime(e.CreateDate), PublisTime: timeValue, Seeders: parseInt(e.Seed), Peers: parseInt(e.Peer), MagnetUri: magnet, Link: e.Link, viewed: viewed(hash), CategoryDesc: e.Categories, checked_at: Date.now(), source_rank: source_rank, hash: hash }; } function callSearch(path, done, fail) { var u = Utils$1.buildUrl(base_url, path, [{ name: 'query', value: params.search }]); network$2["native"](u, function (json) { if (Array.isArray(json)) done(json);else fail(Lang.translate('torrent_parser_request_error') + ' (' + path + ')'); }, function () { fail(Lang.translate('torrent_parser_no_responce') + ' (' + base_url + path + ')'); }); } var calls = []; if (mode == 'rutor' || mode == 'both') calls.push({ path: '/search/', rank: 0 }); if (mode == 'torznab' || mode == 'both') calls.push({ path: '/torznab/search/', rank: 1 }); function runCall(spec, ondone, onfail) { callSearch(spec.path, function (json) { ondone(json.map(function (e) { return mapResult(e, spec.rank); })); }, onfail); } if (calls.length == 1) { runCall(calls[0], function (items) { return oncomplite({ Results: items }); }, onerror); return; } var results = []; var pending = calls.length; var success = false; var first_error; calls.forEach(function (spec, index) { runCall(spec, function (items) { results[index] = items; success = true; if (--pending == 0) { var _ref5; var merged = mergeResults((_ref5 = []).concat.apply(_ref5, _toConsumableArray(results.filter(Boolean)))); oncomplite({ Results: merged }); } }, function (err) { results[index] = null; first_error = first_error || err; if (--pending == 0) { if (success) { var _ref6; var merged = mergeResults((_ref6 = []).concat.apply(_ref6, _toConsumableArray(results.filter(Boolean)))); oncomplite({ Results: merged }); } else onerror(first_error || ''); } }); }); } function clear$3() { network$2.clear(); } var Parser = { init: init$x, get: get$5, jackett: jackett, clear: clear$3 }; var WatchedHistory = /*#__PURE__*/function () { function WatchedHistory(movie) { _classCallCheck(this, WatchedHistory); this.file_id = Utils$1.hash(movie.number_of_seasons ? movie.original_name : movie.original_title); this.html = Template.js('watched_history'); this.update(); } _createClass(WatchedHistory, [{ key: "set", value: function set(_set) { var watched = Storage.cache('online_watched_last', 5000, {}); if (!watched[this.file_id]) watched[this.file_id] = {}; Arrays.extend(watched[this.file_id], _set, true); Storage.set('online_watched_last', watched); this.update(); } }, { key: "get", value: function get() { var watched = Storage.cache('online_watched_last', 5000, {}); return watched[this.file_id]; } }, { key: "update", value: function update() { var watched = this.get(); var body = this.html.find('.watched-history__body').empty(); if (watched) { var line = []; if (watched.balanser_name) line.push(watched.balanser_name); if (watched.voice_name) line.push(watched.voice_name); if (watched.season) line.push(Lang.translate('torrent_serial_season') + ' ' + watched.season); if (watched.episode) line.push(Lang.translate('torrent_serial_episode') + ' ' + watched.episode); line.forEach(function (n) { body.append(Template.elem('span', { text: Utils$1.clearHtmlTags(n).trim() })); }); } else body.append(Template.elem('span', { text: Lang.translate('no_watch_history') })); } }, { key: "render", value: function render(js) { return js ? this.html : $(this.html); } }]); return WatchedHistory; }(); function Listener(movie) { var _self = this; function start(e) { if (e.type == 'list_open') { Lampa.Listener.follow('torrent_file', open); } if (e.type == 'list_close') { Lampa.Listener.remove('torrent_file', open); } } function open(e) { if (e.type == 'onenter') { var open_movie = e.params.movie || {}; if (open_movie.id == movie.id) _self.listener.send('open', e); } } this.listener = Subscribe$2(); this.destroy = function () { Lampa.Listener.remove('torrent_file', start); Lampa.Listener.remove('torrent_file', open); }; Lampa.Listener.follow('torrent_file', start); } var voices$1 = ["Анастасия Гайдаржи + Андрей Юрченко", "Студии Суверенного Лепрозория", "Студия Пиратского Дубляжа", "IgVin & Solncekleshka", "Gremlin Creative Studio", "Alternative Production", "HelloMickey Production", "Bubble Dubbing Company", "Н.Севастьянов seva1988", "XDUB Dorama + Колобок", "Мобильное телевидение", "СПД - Сладкая парочка", "Selena International", "Black Street Records", "Intra Communications", "BBC Saint-Petersburg", "Melodic Voice Studio", "Voice Project Studio", "Несмертельное оружие", "Петербургский дубляж", "Studio Victory Аsia", "Asian Miracle Group", "True Dubbing Studio", "Lizard Cinema Trade", "National Geographic", "Позитив-Мультимедиа", "Премьер Мультимедиа", "Уолт Дисней Компани", "Parovoz Production", "Shadow Dub Project", "Zone Vision Studio", "Анастасия Гайдаржи", "The Kitchen Russia", "Малиновский Сергей", "Family Fan Edition", "Paramount Pictures", "Иванова и П. Пашут", "Так Треба Продакшн", "Хихикающий доктор", "Четыре в квадрате", "Project Web Mania", "Paramount Channel", "Back Board Cinema", "Zoomvision Studio", "Universal Channel", "RedDiamond Studio", "НеЗупиняйПродакшн", "Селена Интернешнл", "Студия «Стартрек»", "Колодій Трейлерів", "Universal Russia", "Paramount Comedy", "Андрей Питерский", "Реальный перевод", "MC Entertainment", "Екатеринбург Арт", "Lucky Production", "Cowabunga Studio", "Анатолий Ашмарин", "Васька Куролесов", "Brain Production", "Квадрат Малевича", "Первый канал ОРТ", "Русский Репортаж", "Сolumbia Service", "Sunshine Studio", "GreenРай Studio", "New Dream Media", "DeadLine Studio", "Воробьев Сергей", "DeeAFilm Studio", "Николай Дроздов", "Денис Шадинский", "Cartoon Network", "Amazing Dubbing", "Volume-6 Studio", "Антонов Николай", "Ульпаней Эльром", "Cinema Prestige", "AnimeSpace Team", "CinemaSET GROUP", "XvidClub Studio", "З Ранку До Ночі", "Максим Логинофф", "Студия Горького", "Ушастая озвучка", "Hamster Studio", "Agatha Studdio", "SunshineStudio", "Kulzvuk Studio", "Вартан Дохалов", "Viasat History", "DIVA Universal", "KosharaSerials", "Julia Prosenuk", "SovetRomantica", "Mallorn Studio", "TUMBLER Studio", "CrazyCatStudio", "Syfy Universal", "Horizon Studio", "Анатолий Гусев", "Максим Жолобов", "RedRussian1337", "Creative Sound", "Garsu Pasaulis", "visanti-vasaer", "GoodTime Media", "Кирдин | Stalk", "Anything-group", "Goodtime Media", "Jakob Bellmann", "Витя «говорун»", "Л. Володарский", "Леша Прапорщик", "Медиа-Комплекс", "Прайд Продакшн", "Русский дубляж", "Союзмультфильм", "Студия Колобок", "Red Head Sound", "LE-Production", "ViruseProject", "Victory-Films", "Jetvis Studio", "Greb&Creative", "5-й канал СПб", "Dream Records", "Filiza Studio", "SHIZA Project", "Bars MacAdams", "Nazel & Freya", "Vulpes Vulpes", "Храм Дорам ТВ", "АРК-ТВ Studio", "Film Prestige", "Rainbow World", "Banyan Studio", "Bonsai Studio", "Мадлен Дюваль", "VO-Production", "Voice Project", "Flarrow Films", "Видеопродакшн", "Хоррор Мэйкер", "Lizard Cinema", "Фортуна-Фильм", "VIP Serial HD", "Старый Бильбо", "Семыкина Юлия", "Штамп Дмитрий", "Arasi project", "ARRU Workshop", "Byako Records", "FiliZa Studio", "Gezell Studio", "HamsterStudio", "PCB Translate", "Renegade Team", "Sci-Fi Russia", "The Mike Rec.", "VO-production", "Мика Бондарик", "Наталья Гурзо", "Премьер Видео", "Трамвай-фильм", "Кубик в Кубе", "Кураж-Бамбей", "Первый канал", "Trdlo.studio", "Студия Райдо", "AniLibria.TV", "RG.Paravozik", "Profix Media", "AlphaProject", "AnimeReactor", "Кармен Видео", "Korean Craze", "Sony Channel", "Train Studio", "Фильмэкспорт", "Кирилл Сагач", "ViP Premiere", "Деваль Видео", "RussianGuy27", "HaseRiLLoPaW", "Сергей Дидок", "Mystery Film", "Psychotronic", "КонтентикOFF", "Говинда Рага", "Horror Maker", "Альтера Парс", "Видеоимпульс", "Мьюзик-трейд", "Тоникс Медиа", "Элегия фильм", "Oneinchnales", "Кинопремьера", "A. Lazarchuk", "Animereactor", "BadCatStudio", "DreamRecords", "General Film", "Ivnet Cinema", "RG Paravozik", "sweet couple", "VictoryFilms", "VulpesVulpes", "Wayland team", "Гей Кино Гид", "Нурмухаметов", "Е. Хрусталёв", "К. Поздняков", "Н. Золотухин", "Новый Дубляж", "Р. Янкелевич", "С. Кузьмичёв", "С. Щегольков", "Синема Трейд", "Синта Рурони", "Точка Zрения", "КОМНАТА ДИДИ", "FocusStudio", "Gears Media", "GladiolusTV", "RecentFilms", "NEON Studio", "Володарский", "Мастер Тэйп", "XDUB Dorama", "Sound-Group", "Sony Sci-Fi", "Good People", "JWA Project", "Nika Lenina", "RiZZ_fisher", "New Records", "КураСгречей", "Неоклассика", "CrezaStudio", "Видеосервис", "BTI Studios", "Eurochannel", "Варус-Видео", "HiWay Grope", "Эй Би Видео", "Nickelodeon", "StudioFilms", "Paul Bunyan", "Inter Video", "Franek Monk", "Другое кино", "Севастьянов", "Lazer Video", "Max Nabokov", "Завгородний", "SnowRecords", "Crunchyroll", "Gold Cinema", "Прямостанов", "Огородников", "Кенс Матвей", "1001 cinema", "Cactus Team", "Description", "DVD Classic", "Gala Voices", "hungry_inri", "Neoclassica", "Oghra-Brown", "Rebel Voice", "Saint Sound", "SakuraNight", "TF-AniGroup", "TrainStudio", "Zone Studio", "Zone Vision", "Варус Видео", "Г. Либергал", "Г. Румянцев", "Е. Гаевский", "И. Сафронов", "И. Степанов", "Лазер Видео", "Малиновский", "Новый Канал", "Петербуржец", "С. Визгунов", "С. Кузнецов", "Студия Трёх", "Цікава Ідея", "Я. Беллманн", "Studio Band", "ApofysTeam", "Карповский", "LevshaFilm", "1001cinema", "CP Digital", "Интерфильм", "Комедия ТВ", "Ох! Студия", "SilverSnow", "NewStation", "StudioBand", "Rain Death", "Первый ТВЧ", "HiWayGrope", "Animegroup", "Shachiburi", "CactusTeam", "Sony Turbo", "AXN Sci-Fi", "Т.О Друзей", "West Video", "East Dream", "Sound Film", "MaxMeister", "VoicePower", "CoralMedia", "VSI Moscow", "VGM Studio", "Студия NLS", "Хуан Рохас", "TatamiFilm", "диктор CDV", "Pazl Voice", "Саня Белый", "Мост-Видео", "AimaksaLTV", "Contentica", "Инфо-фильм", "Электричка", "Бусов Глеб", "AvePremier", "BraveSound", "CinemaTone", "DniproFilm", "ELEKTRI4KA", "eraserhead", "Fox Russia", "Mega-Anime", "MifSnaiper", "Nice-Media", "PiratVoice", "Postmodern", "Reanimedia", "Sky Voices", "SkyeFilmTV", "Костюкевич", "Толстобров", "Б. Федоров", "Ващенко С.", "Глуховский", "Держиморда", "Е. Гранкин", "И. Еремеев", "К. Филонов", "Мост Видео", "Н. Антонов", "Н. Дроздов", "Новый диск", "Переводман", "С. Казаков", "С. Лебедев", "С. Макашов", "Союз Видео", "ТВ XXI век", "Ю. Немахов", "Dream Cast", "Причудики", "NewStudio", "Red Media", "Синема УС", "SDI Media", "CasStudio", "turok1990", "HighHopes", "AniLibria", "FanStudio", "Sedorelli", "Flux-Team", "Kobayashi", "KinoGolos", "Fox Crime", "Discovery", "GREEN TEA", "Persona99", "3df voice", "ShinkaDan", "АрхиТеатр", "СВ-Студия", "FilmsClub", "fiendover", "Воротилин", "LakeFilms", "Кириллица", "AniPLague", "JoyStudio", "Формат AB", "AveBrasil", "Невафильм", "OnisFilms", "Neo-Sound", "Муравский", "BeniAffet", "Янкелевич", "AveDorama", "Киномания", "CBS Drama", "Novamedia", "NewComers", "Ghostface", "Sephiroth", "Andre1288", "DoubleRec", "Astana TV", "Останкино", "Видеобаза", "CLS Media", "Seoul Bay", "Хрусталев", "Золотухин", "Videogram", "AAA-Sound", "Epic Team", "GoodVideo", "Gramalant", "INTERFILM", "Kinomania", "No-Future", "RainDeath", "RATTLEBOX", "Sawyer888", "SmallFilm", "SOLDLUCK2", "SpaceDust", "Timecraft", "Total DVD", "Video-BIZ", "VIZ Media", "Васильцев", "Григорьев", "ААА-sound", "Амальгама", "Весельчак", "Деньщиков", "Шадинский", "ЕА Синема", "Зереницын", "И. Клушин", "Имидж-Арт", "Карапетян", "Машинский", "Мительман", "Рыжий пес", "С. Дьяков", "Самарский", "СВ Студия", "Советский", "Солодухин", "ТО Друзей", "Ю. Сербин", "Ю. Товбин", "AnimeVost", "Omskbird", "LostFilm", "AlexFilm", "IdeaFilm", "ColdFilm", "KinoView", "Jimmy J.", "Дольский", "Гаврилов", "Алексеев", "Визгунов", "Либергал", "Кузнецов", "Горчаков", "Gravi-TV", "Murzilka", "STEPonee", "NovaFilm", "Kerems13", "Fox Life", "AzOnFilm", "SorzTeam", "Гаевский", "СВ-Дубль", "GoldTeam", "DexterTV", "AniMedia", "ANIvoice", "JeFerSon", "RealFake", "AniMaunt", "TurkStar", "Медведев", "FilmGate", "Логинофф", "Loginoff", "Animedub", "GostFilm", "ClubFATE", "Hallmark", "Тимофеев", "Дьяконов", "Лексикон", "Superbit", "VideoBIZ", "WestFilm", "kubik&ko", "Марченко", "Журавлев", "Карусель", "Barin101", "Amalgama", "Кинолюкс", "AB-Video", "Пирамида", "Нарышкин", "Дубровин", "Махонько", "Хлопушка", "АрхиАзия", "Ultradox", "Мельница", "Бессонов", "Бахурани", "Индия ТВ", "AdiSound", "ALEKS KV", "AuraFilm", "DeadLine", "Extrabit", "Foxlight", "GetSmart", "ImageArt", "Marclail", "metalrus", "Milirina", "MiraiDub", "MOYGOLOS", "OMSKBIRD", "Radamant", "RoxMarty", "st.Elrom", "VashMax2", "VendettA", "XL Media", "Артемьев", "Васильев", "Савченко", "Воронцов", "Войсовер", "Домашний", "Е. Лурье", "Е. Рудой", "Ист-Вест", "ЛанселаП", "Ленфильм", "Заугаров", "Мосфильм", "Оверлорд", "С. Рябов", "Супербит", "Толмачев", "Ю. Живов", "Paradox", "BaibaKo", "Jaskier", "Колобок", "Михалев", "Дохалов", "SoftBox", "MUZOBOZ", "ZM-Show", "Levelin", "Немахов", "Яроцкий", "BadBajo", "СВ-Кадр", "Позитив", "RusFilm", "Назаров", "Сыендук", "Яковлев", "Lord32x", "Onibaku", "Trina_D", "Hamster", "AniFilm", "HDrezka", "ShowJet", "BukeDub", "SomeWax", "Anifilm", "TVShows", "РуФилмс", "Пифагор", "AniStar", "Netflix", "Octopus", "MixFilm", "Рутилов", "Elysium", "FireDub", "AveTurk", "Багичев", "Дасевич", "Twister", "Морозов", "Sam2007", "SesDizi", "AnyFilm", "Urasiko", "Wakanim", "Латышев", "Ващенко", "Сонотек", "Никитин", "Сонькин", "Кипарис", "Королёв", "RUSCICO", "Филонов", "Ошурков", "Герусов", "Пятница", "5 канал", "Amalgam", "Anistar", "AniWayt", "datynet", "DeadSno", "Eladiel", "ELYSIUM", "F-TRAIN", "FoxLife", "Janetta", "Kолобок", "LeDoyen", "Liga HQ", "lord666", "Macross", "McElroy", "NemFilm", "OpenDub", "PashaUp", "SOFTBOX", "To4kaTV", "TV 1000", "VicTeam", "ZM-SHOW", "Клюквин", "Матвеев", "Смирнов", "Бибиков", "Абдулов", "Данилов", "sf@irat", "Королев", "Люсьена", "Омикрон", "Парадиз", "Пепелац", "Синхрон", "Сокуров", "Хихидок", "AniBaza", "Ozz.tv", "Сербин", "Кравец", "SNK-TV", "Amedia", "Гоблин", "Kiitos", "Есарев", "Санаев", "Шварко", "Карцев", "Кашкин", "Мудров", "Иванов", "Котова", "Kansai", "ZEE TV", "AniDUB", "Ancord", "Berial", "Cuba77", "OSLIKt", "Tycoon", "Курдов", "Кошкин", "Stevie", "Лагута", "Кондор", "Киреев", "FocusX", "Пронин", "neko64", "Shaman", "GalVid", "D.I.M.", "Н-Кино", "Товбин", "binjak", "Акцент", "Козлов", "Нева-1", "Milvus", "Готлиб", "Zerzia", "Дьяков", "Вольга", "Строев", "Alezan", "ДиоНиК", "Стасюк", "TV1000", "NewDub", "Набиев", "Светла", "Nastia", "Emslie", "100 ТВ", "4u2ges", "Azazel", "BD CEE", "Boльгa", "den904", "Elegia", "Gemini", "Jetvis", "JimmyJ", "KANSAI", "kiitos", "L0cDoG", "LeXiKC", "Lisitz", "madrid", "Mikail", "MrRose", "Ozz TV", "Prolix", "RedDog", "Rumble", "Satkur", "Selena", "Suzaku", "WiaDUB", "WVoice", "Zendos", "Агапов", "Акопян", "Шуваев", "АБыГДе", "Акалит", "Альянс", "Анубис", "Anubis", "Арк-ТВ", "Бойков", "Вихров", "Векшин", "Гризли", "Гундос", "Пучков", "Живаго", "Жучков", "Зебуро", "Килька", "Лапшин", "Лизард", "Миняев", "НЕВА 1", "НЛО-TV", "Ракурс", "Россия", "С.Р.И.", "KOleso", "Гуртом", "ТВ СПб", "Швецов", "OnWave", "DZUSKI", "Kerob", "To4ka", "Чадов", "Живов", "ВГТРК", "Elrom", "Игмар", "Котов", "РенТВ", "Рыбин", "Ozeon", "Cmert", "Штейн", "zamez", "Гланц", "Белов", "Anika", "Lupin", "Ryc99", "ko136", "Рябов", "Amber", "Arisu", "DeMon", "Велес", "Акира", "Ворон", "Рудой", "С.Р.И", "Лайко", "D2Lab", "Jetix", "Попов", "Хабар", "Интер", "AniUA", "D2lab", "erogg", "IНТЕР", "JetiX", "PaDet", "RinGo", "seqw0", "SHIZA", "Solod", "ssvss", "Мишин", "АнВад", "Бигыч", "Рукин", "Штамп", "Новий", "Перец", "Райдо", "ТВЧ 1", "Laci", "ETV+", "Vano", "Jade", "RAIM", "Andy", "Нота", "Твин", "ИДДК", "Voiz", "CPIG", "Dice", "Gits", "ICTV", "jept", "KIHO", "Line", "SGEV", "Tori", "Troy", "Twix", "Чуев", "Инис", "Ирэн", "ТВ-3", "ТВИН", "ДТВ", "FOX", "НТВ", "СТС", "ICG", "ТВЦ", "2x2", "MTV", "Oni", "JAM", "AMS", "DDV", "AMC", "НСТ", "IVI", "КТК", "Че!", "MGM", "МИР", "ТНТ", "FDV", "ТВ3", "LDV", "1+1", "2+2", "2х2", "AOS", "CDV", "MCA", "QTV", "TB5", "VHS", "АМС", "ГКГ", "ИГМ", "НТН", "РТР", "ТВ6", "ТРК", "UKR", "D1", "R5", "К9"]; /** * Torrent Title Decoder * Разбирает название торрента и возвращает структурированные данные * @param {string} title * @returns {{ season: number|null, episodes: string|null, year: number|null, quality: string|null, resolution: string|null }} */ function general(title) { var result = { season: null, seasons: [], episodes: null, year: null, quality: null, resolution: null }; if (!title) return result; // ─── Season ─────────────────────────────────────────────────────────────── // "4 сезон", "4 сезон:" → 4 // "Season 3" → 3 // "[S01-02]" → '1-2' (диапазон сезонов) // "04x01" → 4 (NNxMM) // "S03E01", "S03" → 3 var season_range = title.match(/\[S(\d{1,2})[-–](\d{1,2})\]/i) || title.match(/(?:сезон|season)\s*(\d{1,2})[-–](\d{1,2})/i) || title.match(/(\d{1,2})[-–](\d{1,2})\s*(?:сезон|season)/i); if (season_range) { result.season = parseInt(season_range[1]) + '-' + parseInt(season_range[2]); } else { var season = title.match(/(\d+)\s*(?:сезон|season)/i) || title.match(/(?:сезон|season):?\s*(\d{1,2})/i) || title.match(/(\d{1,2})[nd|rd] season/i) || title.match(/(?:тв|tv)-(\d+)/i) || title.match(/\b(\d{1,2})x\d{1,2}/i) || title.match(/\bs(\d{1,2})e/i) || title.match(/\bs(\d{1,2})\b/i); if (season) result.season = parseInt(season[1]); } if (!result.season) result.season = 1; // Если сезон не указан, предполагаем что это 1-й сезон if (typeof result.season === 'string' && result.season.indexOf('-') !== -1) { var parts = result.season.split('-'); var from = parseInt(parts[0]); var to = parseInt(parts[1]); for (var i = from; i <= to; i++) { result.seasons.push(i); } } else { result.seasons = [parseInt(result.season)]; } // ─── Episodes ───────────────────────────────────────────────────────────── // "04x01-07 из 10" → '1-7' // "1-4 серия из 10" → '1-4' // "[1- из 10]", "(1- из 10)" → '1-10' // "(3-10 из 20)" → '3-10' // "[12 из 12]", "(12 из 12)" → '12' // "S01E05", "E05", "5 серия" → '5' // NNxMM-KK format var ep_nx = title.match(/\d{1,2}x(\d{1,2})[-–](\d{1,2})/i); if (ep_nx) { result.episodes = parseInt(ep_nx[1]) + '-' + parseInt(ep_nx[2]); } else { // Range with optional "серия/из" var ep_range = title.match(/(\d+)\s*[-–]\s*(\d+)\s*(?:серия|episode)/i) || title.match(/(\d{1,3})[-–](\d{1,3})\s*серии/i) || title.match(/[[(](\d{1,3})\s*[-–](\d{1,3})\s*(?:из|з|of)\s*(\d{1,3})/i) || title.match(/[[(](\d{1,3})\s*[-–]\s*(?:из|з|of)\s*(\d{1,3})/i) || title.match(/[[(](\d{1,3})\s*(?:из|з|of)\s*(\d{1,3})[\])]/i) || title.match(/[e](\d{1,3})\s*[-–]\s*(?:(?:из|з|of)\s+)?(\d{1,3})/i) || title.match(/(?:серии|серія|episodes)\s*(\d{1,3})\s*(?:(?:из|з|of)\s+)?(\d{1,3})/i) || title.match(/(?:серии|серія|episodes)\s*(\d{1,3})[-–](\d{1,3})\s*(?:(?:из|з|of)\s+)?(\d{1,3})/i) || title.match(/(?:серии|episodes):\s*(\d+)[-–](\d+)/i); if (ep_range) { // Убеждаемся что это не диапазон годов (1990-2024) var a = parseInt(ep_range[1]); var b = parseInt(ep_range[2]); var looks_like_years = a >= 1900 && b >= 1900; if (!looks_like_years) { result.episodes = a == b ? '1-' + b : a > 1 ? '1-' + a : a + '-' + b; } } else { // Single episode var ep_single = title.match(/\bs\d{1,2}e(\d+)/i) || title.match(/\d{1,2}x(\d{1,2})\b/i) || title.match(/\be(\d{1,2})\b/i) || title.match(/[[(](\d{1,2})\s+(?:из|з|of)\s+\d{1,2}[\])]/) || title.match(/(\d{1,2})\s+(?:из|з|of)\s+\d{1,2}/) || title.match(/(\d+)\s*(?:серия|episode)/i); if (ep_single) result.episodes = '1-' + String(parseInt(ep_single[1])); } } // ─── Year ───────────────────────────────────────────────────────────────── // "(2022-2023)" → 2022 (диапазон лет — берём первый) // "(2024)" → 2024 // bare "2024" → 2024 var year = title.match(/\(((?:19|20)\d{2})[-–](?:19|20)\d{2}\)/) || title.match(/\(((?:19|20)\d{2})\)/) || title.match(/\b((?:19|20)\d{2})\b/); if (year) result.year = parseInt(year[1]); // ─── Quality ────────────────────────────────────────────────────────────── // Порядок важен: более длинные/специфичные варианты идут первыми, // чтобы "WEB-DLRip" не поглощалось "WEB-DL" и т.д. var qualities = ['WEB-DLRip', 'WEB-DL', 'WEBRip', 'WEB', 'Blu-Ray', 'BluRay', 'BDRip', 'HDRip', 'HDTV', 'DVDRip', 'DVD', 'CAM', 'TS']; for (var _i = 0, _qualities = qualities; _i < _qualities.length; _i++) { var q = _qualities[_i]; // Экранируем дефис, добавляем lookahead чтобы "WEB-DL" не совпадало с "WEB-DLRip" var pattern = q.replace(/-/g, '[-]?') + '(?![a-zA-Z])'; if (new RegExp(pattern, 'i').test(title)) { result.quality = q; break; } } // ─── HDR ───────────────────────────────────────────────────────────────── var hdr = title.match(/\b(HDR|Dolby Vision)\b/i); if (hdr) result.hdr = hdr[1]; // ─── Resolution ─────────────────────────────────────────────────────────── var resolution_map = { '4k': '4K', 'uhd': '4K', '2160p': '4K', '1440p': '2K', '1080p': 'FHD', '720p': 'HD', '480p': 'SD', '360p': 'LD' }; var resolution = title.match(/\b(4K|UHD|2160p|1080p|720p|480p|360p)\b/i); if (resolution) result.resolution = resolution_map[resolution[1].toLowerCase()] || resolution[1]; return result; } function voices(title) { if (!title) return []; var lower = title.toLowerCase(); var found = []; voices$1.forEach(function (voice) { if (found.indexOf(voice) === -1 && lower.indexOf(voice.toLowerCase()) !== -1) { found.push(voice); } }); return found; } var TitleParser = { general: general, voices: voices }; var filter_langs = [{ title: '#{filter_lang_ru}', code: 'ru' }, { title: '#{filter_lang_uk}', code: 'uk' }, { title: '#{filter_lang_en}', code: 'en' }, { title: '#{filter_lang_be}', code: 'be' }, { title: '#{filter_lang_zh}', code: 'zh|cn' }, { title: '#{filter_lang_ja}', code: 'ja' }, { title: '#{filter_lang_ko}', code: 'ko' }, { title: '#{filter_lang_af}', code: 'af' }, { title: '#{filter_lang_sq}', code: 'sq' }, { title: '#{filter_lang_ar}', code: 'ar' }, { title: '#{filter_lang_az}', code: 'az' }, { title: '#{filter_lang_hy}', code: 'hy' }, { title: '#{filter_lang_ba}', code: 'ba' }, { title: '#{filter_lang_bg}', code: 'bg' }, { title: '#{filter_lang_bn}', code: 'bn' }, { title: '#{filter_lang_bs}', code: 'bs' }, { title: '#{filter_lang_ca}', code: 'ca' }, { title: '#{filter_lang_ce}', code: 'ce' }, { title: '#{filter_lang_cs}', code: 'cs' }, { title: '#{filter_lang_da}', code: 'da' }, { title: '#{filter_lang_ka}', code: 'ka' }, { title: '#{filter_lang_de}', code: 'de' }, { title: '#{filter_lang_el}', code: 'el' }, { title: '#{filter_lang_es}', code: 'es' }, { title: '#{filter_lang_et}', code: 'et' }, { title: '#{filter_lang_fa}', code: 'fa' }, { title: '#{filter_lang_fi}', code: 'fi' }, { title: '#{filter_lang_fr}', code: 'fr' }, { title: '#{filter_lang_ga}', code: 'ga' }, { title: '#{filter_lang_gl}', code: 'gl' }, { title: '#{filter_lang_gn}', code: 'gn' }, { title: '#{filter_lang_he}', code: 'he' }, { title: '#{filter_lang_hi}', code: 'hi' }, { title: '#{filter_lang_hr}', code: 'hr' }, { title: '#{filter_lang_hu}', code: 'hu' }, { title: '#{filter_lang_id}', code: 'id' }, { title: '#{filter_lang_is}', code: 'is' }, { title: '#{filter_lang_it}', code: 'it' }, { title: '#{filter_lang_kk}', code: 'kk' }, { title: '#{filter_lang_ks}', code: 'ks' }, { title: '#{filter_lang_ku}', code: 'ku' }, { title: '#{filter_lang_ky}', code: 'ky' }, { title: '#{filter_lang_lt}', code: 'lt' }, { title: '#{filter_lang_lv}', code: 'lv' }, { title: '#{filter_lang_mi}', code: 'mi' }, { title: '#{filter_lang_mk}', code: 'mk' }, { title: '#{filter_lang_mn}', code: 'mn' }, { title: '#{filter_lang_mt}', code: 'mt' }, { title: '#{filter_lang_no}', code: 'no|nb|nn' }, { title: '#{filter_lang_ne}', code: 'ne' }, { title: '#{filter_lang_nl}', code: 'nl' }, { title: '#{filter_lang_pa}', code: 'pa' }, { title: '#{filter_lang_pl}', code: 'pl' }, { title: '#{filter_lang_ps}', code: 'ps' }, { title: '#{filter_lang_pt}', code: 'pt' }, { title: '#{filter_lang_ro}', code: 'ro' }, { title: '#{filter_lang_si}', code: 'si' }, { title: '#{filter_lang_sk}', code: 'sk' }, { title: '#{filter_lang_sl}', code: 'sl' }, { title: '#{filter_lang_sm}', code: 'sm' }, { title: '#{filter_lang_so}', code: 'so' }, { title: '#{filter_lang_sr}', code: 'sr' }, { title: '#{filter_lang_sv}', code: 'sv' }, { title: '#{filter_lang_sw}', code: 'sw' }, { title: '#{filter_lang_ta}', code: 'ta' }, { title: '#{filter_lang_tg}', code: 'tg' }, { title: '#{filter_lang_th}', code: 'th' }, { title: '#{filter_lang_tk}', code: 'tk' }, { title: '#{filter_lang_tr}', code: 'tr' }, { title: '#{filter_lang_tt}', code: 'tt' }, { title: '#{filter_lang_ur}', code: 'ur' }, { title: '#{filter_lang_uz}', code: 'uz' }, { title: '#{filter_lang_vi}', code: 'vi' }, { title: '#{filter_lang_yi}', code: 'yi' }]; /** * Компонент "Torrents" * @param {*} object */ function component$h(object) { Arrays.extend(object, { movie: { title: object.search, original_title: object.search }, params: { noinfo: object.from_search ? true : false } }); var network = new Request(); var scroll = new Scroll({ mask: true, over: true }); var files = new Explorer$1(object); var history = new WatchedHistory(object.movie); var filter; var results = []; var filtred = []; var listener; var total_pages = 1; var last; var initialized; var filter_items = { quality: [Lang.translate('torrent_parser_any_one'), '4k', '1080p', '720p'], hdr: [Lang.translate('torrent_parser_no_choice'), Lang.translate('torrent_parser_yes'), Lang.translate('torrent_parser_no')], dv: [Lang.translate('torrent_parser_no_choice'), 'Dolby Vision', 'Dolby Vision TV', Lang.translate('torrent_parser_no')], sub: [Lang.translate('torrent_parser_no_choice'), Lang.translate('torrent_parser_yes'), Lang.translate('torrent_parser_no')], voice: [], tracker: [Lang.translate('torrent_parser_any_two')], year: [Lang.translate('torrent_parser_any_two')], lang: [Lang.translate('torrent_parser_any_two')], _3d: [Lang.translate('torrent_parser_no_choice'), Lang.translate('torrent_parser_yes'), Lang.translate('torrent_parser_no')] }; var filter_translate = { quality: Lang.translate('torrent_parser_quality'), hdr: 'HDR', dv: 'Dolby Vision', sub: Lang.translate('torrent_parser_subs'), voice: Lang.translate('torrent_parser_voice'), tracker: Lang.translate('torrent_parser_tracker'), year: Lang.translate('torrent_parser_year'), season: Lang.translate('torrent_parser_season'), lang: Lang.translate('title_language_short'), _3d: '3D' }; var filter_multiple = ['quality', 'voice', 'tracker', 'season', 'lang']; var sort_translate = { popular: Lang.translate('title_popular'), Seeders: Lang.translate('torrent_parser_sort_by_seeders'), Size: Lang.translate('torrent_parser_sort_by_size'), Title: Lang.translate('torrent_parser_sort_by_name'), Tracker: Lang.translate('torrent_parser_sort_by_tracker'), PublisTime: Lang.translate('torrent_parser_sort_by_date'), viewed: Lang.translate('torrent_parser_sort_by_viewed') }; var i = 20, y = new Date().getFullYear(); while (i--) { filter_items.year.push(y - (19 - i) + ''); } var finded_seasons = []; var finded_seasons_full = []; filter_items.lang = filter_items.lang.concat(filter_langs.map(function (a) { return Lang.translate(a.title); })); scroll.minus(files.render().find('.explorer__files-head')); scroll.body().addClass('torrent-list'); if (object.from_search) object.movie.original_title = ''; this.create = function () { return this.render(); }; this.initialize = function () { var _this = this; this.activity.loader(true); if ((object.movie.original_language == 'ja' || object.movie.original_language == 'zh') && object.movie.genres.find(function (g) { return g.id == 16; }) && Storage.field('language') !== 'en') { network.silent(TMDB$1.api((object.movie.name ? 'tv' : 'movie') + '/' + object.movie.id + '?api_key=' + TMDB$1.key() + '&language=en'), function (result) { object.search_two = result.name || result.title; _this.parse(); }, this.parse.bind(this)); } else { this.parse(); } scroll.onEnd = this.next.bind(this); listener = new Listener(object.movie); listener.listener.follow('open', function (e) { if (object.movie.original_name) { history.set({ balanser_name: 'Torrent', season: e.element.season, episode: e.element.episode }); } else { history.set({ balanser_name: 'Torrent' }); } }); return this.render(); }; this.parse = function () { var _this2 = this; filter = new Filter(object); Parser.get(object, function (data) { results = data; results.Results.forEach(function (element) { element.general = TitleParser.general(element.Title.toLowerCase()); }); _this2.build(); Layer.update(scroll.render(true)); _this2.activity.loader(false); _this2.activity.toggle(); }, function (text) { _this2.empty(Lang.translate('torrent_error_connect') + ': ' + text); }); filter.onSearch = function (value) { var extra = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; Activity.replace({ search: value, clarification: true, global: extra.global }); }; filter.onBack = function () { _this2.start(); }; filter.render().find('.selector').on('hover:focus', function (e) { }); filter.addButtonBack(); files.appendHead(filter.render()); }; this.empty = function (descr, add_button) { var em_params = { descr: descr }; if (add_button) { em_params.buttons = [{ title: Lang.translate('filter_clarify'), onEnter: function onEnter() { filter.render().find('.filter--search').trigger('hover:enter'); } }]; } var empty = new Empty(em_params); files.render().find('.explorer__files-head').addClass('hide'); files.appendFiles(empty.render(filter.empty())); scroll.body().removeClass('torrent-list'); this.start = empty.start.bind(empty); this.activity.loader(false); this.activity.toggle(); }; this.listEmpty = function () { var em = Template.get('empty_filter'); var bn = $('
    ' + Lang.translate('filter_clarify') + '
    '); bn.on('hover:enter', function () { filter.render().find('.filter--filter').trigger('hover:enter'); }); em.find('.empty-filter__subtitle').text(Lang.translate('empty_text')); em.find('.empty-filter__title').remove(); em.find('.empty-filter__buttons').removeClass('hide').append(bn); scroll.body().removeClass('torrent-list'); scroll.append(em); }; this.buildSorted = function () { var need = Storage.get('torrents_sort', 'popular'); var select = [{ title: Lang.translate('title_popular'), sort: 'popular' }, { title: Lang.translate('torrent_parser_sort_by_seeders'), sort: 'Seeders' }, { title: Lang.translate('torrent_parser_sort_by_size'), sort: 'Size' }, { title: Lang.translate('torrent_parser_sort_by_name'), sort: 'Title' }, { title: Lang.translate('torrent_parser_sort_by_tracker'), sort: 'Tracker' }, { title: Lang.translate('torrent_parser_sort_by_date'), sort: 'PublisTime' }, { title: Lang.translate('torrent_parser_sort_by_viewed'), sort: 'viewed' }]; select.forEach(function (element) { if (element.sort == need) element.selected = true; }); this.sortResults(need); filter.set('sort', select); this.selectedSort(); }; this.sortResults = function (need) { if (object.movie.number_of_seasons && need == 'popular') { results.Results.sort(function (a, b) { var s_a = parseInt(String(a.general.season).split('-').pop()); var s_b = parseInt(String(b.general.season).split('-').pop()); var e_a = a.general.episodes ? parseInt(String(a.general.episodes).split('-').pop()) : 0; var e_b = b.general.episodes ? parseInt(String(b.general.episodes).split('-').pop()) : 0; return s_b - s_a || e_b - e_a || b.Seeders - a.Seeders; }); } else if (need == 'popular') filter.sort(results.Results, 'Seeders');else filter.sort(results.Results, need); this.sortWithViewed(); }; this.sortWithViewed = function () { var popular = []; var other = []; results.Results.forEach(function (a) { if (a.viewed) popular.push(a);else other.push(a); }); popular.sort(function (a, b) { return b.Seeders - a.Seeders; }); results.Results = popular.concat(other); }; this.cardID = function () { return object.movie.id + ':' + (object.movie.number_of_seasons ? 'tv' : 'movie'); }; this.getFilterData = function () { var all = Storage.cache('torrents_filter_data', 500, {}); var cid = this.cardID(); return all[cid] || Storage.get('torrents_filter', '{}'); }; this.setFilterData = function (filter) { var all = Storage.cache('torrents_filter_data', 500, {}); var cid = this.cardID(); all[cid] = filter; Storage.set('torrents_filter_data', all); Storage.set('torrents_filter', filter); }; this.buildFilterd = function () { var need = this.getFilterData(); var select = []; var add = function add(type, title) { var items = filter_items[type]; var subitems = []; var multiple = filter_multiple.indexOf(type) >= 0; var value = need[type]; if (multiple) value = Arrays.toArray(value); items.forEach(function (name, i) { subitems.push({ title: name, //selected: multiple ? i == 0 : value == i, checked: multiple && value.indexOf(name) >= 0, checkbox: multiple && i > 0, noselect: true, index: i }); }); select.push({ title: title, subtitle: multiple ? value.length ? value.join(', ') : items[0] : typeof value == 'undefined' ? items[0] : items[value], items: subitems, noselect: true, stype: type }); }; filter_items.voice = [Lang.translate('torrent_parser_any_two'), Lang.translate('torrent_parser_voice_dubbing'), Lang.translate('torrent_parser_voice_polyphonic'), Lang.translate('torrent_parser_voice_two'), Lang.translate('torrent_parser_voice_amateur')]; filter_items.tracker = [Lang.translate('torrent_parser_any_two')]; filter_items.season = [Lang.translate('torrent_parser_any_two')]; results.Results.forEach(function (element) { var title = element.Title.toLowerCase(), tracker = element.Tracker; for (var _i = 0; _i < voices$1.length; _i++) { var voice = voices$1[_i].toLowerCase(); if (title.indexOf(voice) >= 0) { if (filter_items.voice.indexOf(voices$1[_i]) == -1) filter_items.voice.push(voices$1[_i]); } if (!element.info) { element.info = { voices: TitleParser.voices(element.Title) }; } if (element.info && element.info.voices) { if (element.info.voices.map(function (v) { return v.toLowerCase(); }).indexOf(voice) >= 0) { if (filter_items.voice.indexOf(voices$1[_i]) == -1) filter_items.voice.push(voices$1[_i]); } } } tracker.split(',').forEach(function (t) { if (filter_items.tracker.indexOf(t.trim()) === -1) filter_items.tracker.push(t.trim()); }); element.general.seasons.forEach(function (season) { var number = season + ''; if (finded_seasons.indexOf(number) == -1) { finded_seasons.push(number); finded_seasons_full.push(number); } }); }); finded_seasons_full.sort(function (a, b) { var ac = parseInt(a); var bc = parseInt(b); if (ac > bc) return 1;else if (ac < bc) return -1;else return 0; }); finded_seasons.sort(function (a, b) { var ac = parseInt(a); var bc = parseInt(b); if (ac > bc) return 1;else if (ac < bc) return -1;else return 0; }); if (finded_seasons.length) filter_items.season = filter_items.season.concat(finded_seasons); // Надо очистить от отсутствующих ключей need.voice = Arrays.removeNoIncludes(Arrays.toArray(need.voice), filter_items.voice); need.tracker = Arrays.removeNoIncludes(Arrays.toArray(need.tracker), filter_items.tracker); need.season = Arrays.removeNoIncludes(Arrays.toArray(need.season), filter_items.season); this.setFilterData(need); select.push({ title: Lang.translate('torrent_parser_reset'), reset: true }); add('quality', Lang.translate('torrent_parser_quality')); add('hdr', 'HDR'); add('dv', 'Dolby Vision'); add('sub', Lang.translate('torrent_parser_subs')); add('voice', Lang.translate('torrent_parser_voice')); add('lang', Lang.translate('title_language_short')); add('season', Lang.translate('torrent_parser_season')); add('tracker', Lang.translate('torrent_parser_tracker')); add('year', Lang.translate('torrent_parser_year')); add('_3d', '3D'); filter.set('filter', select); this.selectedFilter(); }; this.selectedFilter = function () { var need = this.getFilterData(), select = []; for (var _i2 in need) { if (need[_i2]) { if (Arrays.isArray(need[_i2])) { if (need[_i2].length) select.push(filter_translate[_i2] + ':' + need[_i2].join(', ')); } else { select.push(filter_translate[_i2] + ': ' + filter_items[_i2][need[_i2]]); } } } filter.chosen('filter', select); }; this.selectedSort = function () { var select = Storage.get('torrents_sort', 'popular'); filter.chosen('sort', [sort_translate[select]]); }; this.build = function () { var _this3 = this; this.buildSorted(); this.buildFilterd(); this.filtred(); filter.onSelect = function (type, a, b) { if (type == 'sort') { Storage.set('torrents_sort', a.sort); _this3.sortResults(a.sort); } else { if (a.reset) { _this3.setFilterData({}); _this3.buildFilterd(); } else { a.items.forEach(function (n) { return n.checked = false; }); var filter_data = _this3.getFilterData(); filter_data[a.stype] = filter_multiple.indexOf(a.stype) >= 0 ? [] : b.index; a.subtitle = b.title; _this3.setFilterData(filter_data); } } _this3.applyFilter(); _this3.start(); }; filter.onCheck = function (type, a, b) { var data = _this3.getFilterData(), need = Arrays.toArray(data[a.stype]); if (b.checked && need.indexOf(b.title)) need.push(b.title);else if (!b.checked) Arrays.remove(need, b.title); data[a.stype] = need; _this3.setFilterData(data); a.subtitle = need.length ? need.join(', ') : a.items[0].title; _this3.applyFilter(); }; this.showResults(); }; this.applyFilter = function () { this.filtred(); this.selectedFilter(); this.selectedSort(); this.reset(); this.showResults(); last = scroll.render().find('.torrent-item:eq(0)')[0]; if (last) scroll.update(last);else scroll.reset(); }; this.filtred = function () { var filter_data = this.getFilterData(); var filter_any = false; for (var _i3 in filter_data) { var filr = filter_data[_i3]; if (filr) { if (Arrays.isArray(filr)) { if (filr.length) filter_any = true; } else filter_any = true; } } filtred = results.Results.filter(function (element) { if (filter_any) { var passed = false, nopass = false, title = element.Title.toLowerCase(), tracker = element.Tracker; var qua = Arrays.toArray(filter_data.quality), hdr = filter_data.hdr, dv = filter_data.dv, sub = filter_data.sub, voi = Arrays.toArray(filter_data.voice), tra = Arrays.toArray(filter_data.tracker), ses = Arrays.toArray(filter_data.season), lng = Arrays.toArray(filter_data.lang), yer = filter_data.year, _3d = filter_data._3d; var test = function test(search, test_index) { var regex = new RegExp(search); return test_index ? title.indexOf(search) >= 0 : regex.test(title); }; var check = function check(search, invert) { if (test(search)) { if (invert) nopass = true;else passed = true; } else { if (invert) passed = true;else nopass = true; } }; var includes = function includes(type, arr) { if (!arr.length) return; var any = false; arr.forEach(function (a) { if (type == 'quality') { if (a == '4k' && test('(4k|uhd)[ |\\]|,|$]|2160[pр]|ultrahd')) any = true; if (a == '1080p' && test('fullhd|1080[pр]')) any = true; if (a == '720p' && test('720[pр]')) any = true; } if (type == 'voice') { var p = filter_items.voice.indexOf(a); var n = element.info && element.info.voices ? element.info.voices.map(function (v) { return v.toLowerCase(); }) : []; if (p == 1) { if (test('дублирован|дубляж| apple| dub| d[,| |$]|[,|\\s]дб[,|\\s|$]')) any = true; } else if (p == 2) { if (test('многоголос| p[,| |$]|[,|\\s](лм|пм)[,|\\s|$]')) any = true; } else if (p == 3) { if (test('двухголос|двуголос| l2[,| |$]|[,|\\s](лд|пд)[,|\\s|$]')) any = true; } else if (p == 4) { if (test('любитель|авторский| l1[,| |$]|[,|\\s](ло|ап)[,|\\s|$]')) any = true; } else if (test(a.toLowerCase(), true)) any = true;else if (n.length && n.indexOf(a.toLowerCase()) >= 0) any = true; } if (type == 'lang') { var _p = filter_items.lang.indexOf(a); var c = filter_langs[_p - 1]; if (c) { if (element.languages) { if (element.languages.find(function (l) { return l.toLowerCase().slice(0, 2) == c.code; })) any = true; } else if (title.indexOf(c.code) >= 0) any = true; } else any = true; } if (type == 'tracker') { if (tracker.split(',').find(function (t) { return t.trim().toLowerCase() == a.toLowerCase(); })) any = true; } if (type == 'season') { var _i4 = finded_seasons.indexOf(a); var f = parseInt(finded_seasons_full[_i4]); if (element.general.seasons.indexOf(f) >= 0) any = true; } }); if (any) passed = true;else nopass = true; }; includes('quality', qua); includes('voice', voi); includes('tracker', tra); includes('season', ses); includes('lang', lng); if (hdr) check('[\\[| ]hdr[10| |\\]|,|$]', hdr !== 1); if (dv == 0) { check(filter_items.dv[dv], dv !== 1); } else if (dv == 1) { check('dolby vision'); } else if (dv == 2) { check('dolby vision tv'); } else if (dv == 3) { check('dolby vision', dv !== 0); } if (sub) check(' sub|[,|\\s]ст[,|\\s|$]', sub !== 1); if (yer) { check(filter_items.year[yer]); } if (_3d) check(' стереопара|interlace|anaglyph|анаглиф|bd3d|over\\-?under|side\\-?by\\-?side|[\\-\\[\\(| ]((half|h)?ou|(half|h)?sbs|lrq?|abq?|ba|rl|3d[\\- ]video)([ |\\]\\),]|$)', _3d !== 1); return nopass ? false : passed; } else return true; }); }; this.showResults = function () { total_pages = Math.ceil(filtred.length / 20); if (filtred.length) { scroll.body().addClass('torrent-list'); scroll.append(history.render(true)); this.append(filtred.slice(0, 20)); } else { if (results.Results.length) this.listEmpty();else this.empty(Lang.translate('search_nofound'), true); } files.appendFiles(scroll.render()); }; this.reset = function () { last = false; scroll.clear(); }; this.next = function () { if (object.page < 15 && object.page < total_pages) { object.page++; var offset = (object.page - 1) * 20; this.append(filtred.slice(offset, offset + 20), true); } }; this.mark = function (element, item, add) { var viewed = Storage.cache('torrents_view', 5000, []); if (add) { if (viewed.indexOf(element.hash) == -1) { viewed.push(element.hash); item.append('
    ' + Template.get('icon_viewed', {}, true) + '
    '); } } else { element.viewed = true; Arrays.remove(viewed, element.hash); item.find('.torrent-item__viewed').remove(); } element.viewed = add; Storage.set('torrents_view', viewed); if (!add) Storage.remove('torrents_view', element.hash); }; this.addToBase = function (element) { Torserver.add({ poster: object.movie.img, title: object.movie.title + ' / ' + object.movie.original_title, link: element.MagnetUri || element.Link, data: { lampa: true, movie: object.movie } }, function () { Noty.show(object.movie.title + ' - ' + Lang.translate('torrent_parser_added_to_mytorrents')); }); }; this.append = function (items, append) { var _this4 = this; items.forEach(function (element) { var date = Utils$1.parseTime(element.PublishDate); var bitrate = object.movie.runtime ? Utils$1.calcBitrate(element.Size, object.movie.runtime) : 0; Arrays.extend(element, { title: element.Title, date: date.full, tracker: element.Tracker, bitrate: bitrate, size: !isNaN(parseInt(element.Size)) ? Utils$1.bytesToSize(element.Size) : element.size, seeds: element.Seeders, grabs: element.Peers }); var item = Template.get('torrent', element); var need_remove_ffprobe = false; if (!element.ffprobe) { element.ffprobe = []; need_remove_ffprobe = true; } if (element.ffprobe) { var ffprobe_elem = item.find('.torrent-item__ffprobe'); var ffprobe_tags = []; var general = element.general || {}; var quality = general.resolution || (element.info && element.info.quality ? Utils$1.qualityToText(element.info.quality + 'p') : ''); if (object.movie.number_of_seasons && general.season) { ffprobe_elem.append('
    S' + general.season + '
    ' + (general.episodes ? '
    ' + general.episodes + '
    ' : '') + '
    '); } if (quality) ffprobe_tags.push({ media: 'resolution', value: quality }); if (general.hdr) ffprobe_tags.push({ media: 'resolution', value: 'HDR' }); var video = element.ffprobe.find(function (a) { return a.codec_type == 'video'; }); if (!quality && video) ffprobe_tags.push({ media: 'resolution', value: Utils$1.qualityToText(Utils$1.resolutionToQuality(video.width, video.height, 'p')) }); var audio = element.ffprobe.filter(function (a) { return a.codec_type == 'audio' && a.tags; }); var subs = element.ffprobe.filter(function (a) { return a.codec_type == 'subtitle' && a.tags; }); var voice = Arrays.clone(element.info && element.info.voices ? element.info.voices : []); if (video) ffprobe_tags.push({ media: 'video', value: video.width + 'x' + video.height }); var is_71 = element.ffprobe.find(function (a) { return a.codec_type == 'audio' && a.channels == 8; }); var is_51 = element.ffprobe.find(function (a) { return a.codec_type == 'audio' && a.channels == 6; }); if (is_71) ffprobe_tags.push({ media: 'channels', value: '7.1' }); if (is_51) ffprobe_tags.push({ media: 'channels', value: '5.1' }); audio.forEach(function (a) { var line = []; var lang = (a.tags.language || '').toUpperCase(); var name = a.tags.title || a.tags.handler_name || ''; var _short = voices$1.find(function (v) { return name.toLowerCase().indexOf(v.toLowerCase()) >= 0; }); if (_short) name = _short; var find = voice.find(function (v) { return name.toLowerCase().indexOf(v.toLowerCase()) >= 0; }); if (find) Arrays.remove(voice, find); if (lang) line.push(lang); if (name && lang !== 'ENG') { name = find || name; if (name.toLowerCase().indexOf('dub') >= 0 || name.toLowerCase() == 'd') name = Lang.translate('torrent_parser_voice_dubbing'); line.push(Utils$1.shortText(Utils$1.capitalizeFirstLetter(name), 20)); } if (line.length) ffprobe_tags.push({ media: 'audio', value: line.join(' - ') }); }); voice.forEach(function (v) { ffprobe_tags.push({ media: 'audio', value: v }); }); var find_subtitles = []; subs.forEach(function (a) { var lang = (a.tags.language || '').toUpperCase(); if (lang) find_subtitles.push(lang); }); find_subtitles = find_subtitles.filter(function (el, pos) { return find_subtitles.indexOf(el) == pos; }); find_subtitles.slice(0, 4).forEach(function (a) { ffprobe_tags.push({ media: 'subtitle', value: a }); }); if (find_subtitles.length > 4) ffprobe_tags.push({ media: 'subtitle', value: '+' + (find_subtitles.length - 4) }); ffprobe_tags = ffprobe_tags.filter(function (el, pos) { return ffprobe_tags.map(function (a) { return a.value + a.media; }).indexOf(el.value + el.media) == pos; }); ffprobe_tags.forEach(function (tag) { ffprobe_elem.append('
    ' + tag.value + '
    '); }); if ($('> div', ffprobe_elem).length) ffprobe_elem.removeClass('hide'); if (need_remove_ffprobe) delete element.ffprobe; } if (!bitrate) item.find('.bitrate').remove(); if (element.viewed) item.append('
    ' + Template.get('icon_viewed', {}, true) + '
    '); if (!element.size || parseInt(element.size) == 0) item.find('.torrent-item__size').remove(); item.on('hover:focus', function (e) { last = e.target; scroll.update($(e.target), true); }).on('hover:hover hover:touch', function (e) { last = e.target; Navigator.focused(last); }).on('hover:enter', function (e) { last = e.target; Torrent.opened(function () { _this4.mark(element, item, true); }); element.poster = object.movie.img; _this4.start(); Torrent.start(element, object.movie); Lampa.Listener.send('torrent', { type: 'onenter', element: element, item: item }); }).on('hover:long', function () { var enabled = Controller.enabled().name; var menu = [{ title: Lang.translate('torrent_parser_add_to_mytorrents'), tomy: true }, { title: Lang.translate('torrent_parser_label_title'), subtitle: Lang.translate('torrent_parser_label_descr'), mark: true }, { title: Lang.translate('torrent_parser_label_cancel_title'), subtitle: Lang.translate('torrent_parser_label_cancel_descr'), unmark: true }]; Lampa.Listener.send('torrent', { type: 'onlong', element: element, item: item, menu: menu }); Select.show({ title: Lang.translate('title_action'), items: menu, onBack: function onBack() { Controller.toggle(enabled); }, onSelect: function onSelect(a) { if (a.tomy) { _this4.addToBase(element); } else if (a.mark) { _this4.mark(element, item, true); } else if (a.unmark) { _this4.mark(element, item, false); } Controller.toggle(enabled); } }); }); Lampa.Listener.send('torrent', { type: 'render', element: element, item: item }); scroll.append(item); if (append) Controller.collectionAppend(item); }); }; this.back = function () { Activity.backward(); }; this.start = function () { if (!initialized) { initialized = true; this.initialize(); } Background.immediately(Utils$1.cardImgBackgroundBlur(object.movie)); Controller.add('content', { toggle: function toggle() { Controller.collectionSet(scroll.render(), files.render(true)); Controller.collectionFocus(last || false, scroll.render(true)); Navigator.remove(files.render().find('.explorer-card__head-img')[0]); }, update: function update() {}, up: function up() { if (Navigator.canmove('up')) { Navigator.move('up'); } else Controller.toggle('head'); }, down: function down() { Navigator.move('down'); }, right: function right() { if (Navigator.canmove('right')) Navigator.move('right');else filter.render().find('.filter--filter').trigger('hover:enter'); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else files.toggle(); }, back: this.back }); Controller.toggle('content'); }; this.pause = function () { listener && listener.destroy(); }; this.stop = function () {}; this.render = function () { return files.render(); }; this.destroy = function () { network.clear(); Parser.clear(); files.destroy(); scroll.destroy(); listener && listener.destroy(); results = null; network = null; }; } /** * Компонент "Мои торренты" * @param {*} object * @returns */ function component$g(object) { var comp = Utils$1.createInstance(Full, object, { empty: { router: 'mytorrents' } }); comp.use(Router, 0); comp.use({ onCreate: function onCreate() { var _this = this; Torserver.my(function (result) { result.forEach(function (item) { item.title = item.title.replace('[LAMPA] ', ''); item.data = Arrays.decodeJson(item.data, {}); if (item.data.movie && item.data.movie.poster) { item.poster = item.data.movie.poster; } if (item.data.movie && item.data.movie.release_date) { item.release_date = item.data.movie.release_date; } if (item.data.movie && item.data.movie.first_air_date) { item.first_air_date = item.data.movie.first_air_date; } if (item.data.movie && item.data.movie.original_name) { item.original_name = item.data.movie.original_name; } item.params = { module: Helper$9.only('Card', 'Release', 'Callback', 'Icons') }; }); _this.build({ results: result }); }, this.empty.bind(this)); }, onInstance: function onInstance(item, data) { item.use({ onEnter: function onEnter() { if (!this.disabled) Torrent.open(data.hash, data.data.lampa && data.data.movie ? data.data.movie : false); }, onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); }, onLong: function onLong() { if (this.disabled) return; var enabled = Controller.enabled().name; var menu = []; if (data.data.movie) { menu.push({ title: Lang.translate('title_card'), onSelect: router.call.bind(router, 'full', data.data.movie) }); } menu.push({ title: Lang.translate('torrent_remove_title'), subtitle: Lang.translate('torrent_remove_descr'), onSelect: function onSelect() { Torserver.remove(data.hash); item.disable(); Controller.toggle(enabled); } }); Lampa.Listener.send('mytorrents', { type: 'onlong', object: data, menu: menu }); Select.show({ title: Lang.translate('title_action'), items: menu, onBack: function onBack() { Controller.toggle(enabled); } }); } }); } }); return comp; } /** * Компонент "Релизы" * @param {*} object * @returns */ function component$f(object) { var comp = Utils$1.createInstance(Full, object, { module: Helper$4.toggle(Helper$4.MASK.base, 'Pagination') }); comp.use({ onCreate: function onCreate() { Api.relise(object, this.build.bind(this), this.empty.bind(this)); }, onNext: function onNext(resolve, reject) { Api.relise(object, resolve.bind(this), reject.bind(this)); }, onInstance: function onInstance(item, data) { item.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); return comp; } /** * Компонент "Нет контента" * @param {*} object */ var Component$1 = /*#__PURE__*/function (_Emit) { _inherits(Component, _Emit); var _super = _createSuper(Component); function Component(object) { var _this; _classCallCheck(this, Component); _this = _super.call(this); _this.object = object || {}; _this.emit('init'); return _this; } _createClass(Component, [{ key: "create", value: function create() { this.html = $('
    '); this.empty = new Empty({}); this.html.append(this.empty.render()); this.start = this.empty.start.bind(this.empty); this.emit('create'); this.activity.loader(false); this.activity.toggle(); } }, { key: "render", value: function render() { return this.html; } }, { key: "destroy", value: function destroy() { this.html.remove(); this.emit('destroy'); } }]); return Component; }(Emit); function component$e(object) { var _this2 = this; var scroll = new Scroll({ mask: true, over: true, step: 300 }); var html = $('
    '); var body = $('
    '); var cards = Favorite.full().card; var table = TimeTable.all(); var last; var cards_map = {}; this.create = function () { var _this = this; if (Account$1.Permit.sync) cards = Account$1.Bookmarks.all(); cards.forEach(function (card) { cards_map[card.id] = card; }); if (table.length) { var date_max = 0; var date_now = new Date(); var date_end = new Date(); var date_one = 24 * 60 * 60 * 1000; table.forEach(function (elem) { var episodes = _this.episodes(elem.episodes, elem.next); episodes.forEach(function (ep) { var air = Utils$1.parseToDate(ep.air_date); var tim = air.getTime(); if (date_max < tim) { date_max = tim; date_end = air; } }); }); var date_dif = Math.max(30, Math.min(30, Math.round(Math.abs((date_now - date_end) / date_one)))); if (date_dif > 0) { for (var i = 0; i < date_dif; i++) { this.append(date_now); date_now.setDate(date_now.getDate() + 1); } scroll.minus(); scroll.append(body); html.append(scroll.render()); } else this.empty(); } else this.empty(); this.activity.loader(false); this.activity.toggle(); return this.render(); }; this.episodes = function (episodes, next) { var result = [].concat(episodes); if (next && !result.find(function (e) { return e.air_date == next.air_date; })) { result.push(next); } return result; }; this.empty = function () { var empty = new Empty({ descr: Lang.translate('timetable_empty') }); html.append(empty.render()); _this2.start = empty.start.bind(empty); _this2.activity.loader(false); _this2.activity.toggle(); }; this.append = function (date) { var _this3 = this; var item = $("\n
    \n
    \n
    \n
    \n
    \n
    \n "); var air_date = date.getFullYear() + '-' + ('0' + (date.getMonth() + 1)).slice(-2) + '-' + ('0' + date.getDate()).slice(-2); var air_epis = []; var day_week = Utils$1.parseTime(date.getTime()); var weeks = [Lang.translate('week_7'), Lang.translate('week_1'), Lang.translate('week_2'), Lang.translate('week_3'), Lang.translate('week_4'), Lang.translate('week_5'), Lang.translate('week_6')]; table.forEach(function (elem) { var episodes = _this3.episodes(elem.episodes, elem.next); episodes.forEach(function (ep) { var card = cards_map[elem.id]; if (ep.air_date == air_date && card) { air_epis.push({ episode: ep, card: card }); } }); }); if (air_epis.length) { air_epis.slice(0, 3).forEach(function (elem) { item.find('.timetable__body').append('
    ' + elem.card.name + '
    '); }); if (air_epis.length > 3) { item.find('.timetable__body').append('
    +' + (air_epis.length - 3) + '
    '); } if (air_epis.length == 1) { var preview = $('
    ' + (air_epis[0].episode.name || Lang.translate('noname')) + '
    '); Utils$1.imgLoad(preview.find('img'), TMDB$1.image('t/p/w200/' + air_epis[0].episode.still_path), false, function () { preview.find('img').remove(); }); item.find('.timetable__body').prepend(preview); } item.addClass('timetable__item--any'); } item.find('.timetable__date').text(day_week["short"] + ' - ' + weeks[date.getDay()] + '.'); item.on('hover:focus', function () { last = $(this)[0]; scroll.update($(this)); }).on('hover:hover', function () { last = $(this)[0]; Navigator.focused(last); }).on('hover:enter', function () { last = $(this)[0]; var modal = $('
    '); air_epis.forEach(function (elem) { var foot = $(''); var noty = Template.get('notice_card', { time: Utils$1.parseTime(air_date).full, title: elem.card.name, descr: Lang.translate('card_new_episode') }); foot.append('
    S - ' + elem.episode.season_number + '
    '); foot.append('
    E - ' + elem.episode.episode_number + '
    '); noty.find('.notice__descr').append(foot); Utils$1.imgLoad(noty.find('img'), TMDB$1.image('t/p/w200/' + elem.card.poster_path), function () { noty.addClass('image--loaded'); }); noty.on('hover:enter', function () { Modal.close(); Activity.push({ url: '', component: 'full', id: elem.card.id, method: 'tv', card: elem.card, source: elem.card.source }); }); modal.append(noty); }); Modal.open({ title: Lang.translate('menu_tv'), size: 'medium', html: modal, onBack: function onBack() { Modal.close(); Controller.toggle('content'); } }); }); body.append(item); }; this.back = function () { Activity.backward(); }; this.start = function () { Controller.add('content', { link: this, toggle: function toggle() { Controller.collectionSet(scroll.render()); Controller.collectionFocus(last || false, scroll.render()); Background.change(TMDB$1.image('t/p/w200/oXPYD4c3bLtfAS2FzwjZh7NWqo4.jpg')); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else Controller.toggle('menu'); }, right: function right() { Navigator.move('right'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('head'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down'); }, back: this.back }); Controller.toggle('content'); }; this.pause = function () {}; this.stop = function () {}; this.render = function () { return html; }; this.destroy = function () { scroll.destroy(); html.remove(); }; } /** * Компонент "Подписки" * @param {*} object * @returns */ function component$d(object) { var comp = Utils$1.createInstance(Full, object, { empty: { account: true, router: 'subscribe' } }); comp.use(Router, 0); comp.use({ onCreate: function onCreate() { var _this = this; Account$1.Api.subscribes({ to_card_subscribe: true }, function (data) { data.results.forEach(function (card) { card.params = { module: Helper$9.toggle(Helper$9.MASK.base, 'Subscribe') }; }); _this.build(data); }, this.empty.bind(this)); }, onInstance: function onInstance(item, data) { item.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); return comp; } var About = { onCreate: function onCreate() { this.html = Template.js('company', { name: this.data.name, place: (this.data.headquarters ? this.data.headquarters + (this.data.origin_country ? ', ' : '') : '') + (this.data.origin_country ? this.data.origin_country : '') }); Utils$1.imgLoad(this.html.find('img'), this.data.logo_path ? TMDB.img(this.data.logo_path, 'w500') : this.data.img || 'img/img_broken.svg', function (img) { img.addClass('loaded'); }); } }; var CompanyMap = { About: About }; var Company = /*#__PURE__*/function (_Constructor) { _inherits(Company, _Constructor); var _super = _createSuper(Company); function Company() { _classCallCheck(this, Company); return _super.apply(this, arguments); } return Company; }(Constructor(CompanyMap)); var Helper$3 = new Mask(Arrays.getKeys(CompanyMap)); Helper$3.MASK.base = Helper$3.MASK.none; function component$c(object) { var comp = Utils$1.createInstance(Full$1, object); comp.use({ onCreate: function onCreate() { var _this = this; Api.company(object, function (data) { object.company = data.company; Arrays.insert(data.lines, 0, { results: [], params: { module: Helper$b.MASK.none, emit: { onCreate: function onCreate() { this.company = Utils$1.createInstance(Company, object.company, { module: Helper$3.only('About') }); this.company.create(); this.scroll.append(this.company.render(true)); }, onDestroy: function onDestroy() { var _this$company; (_this$company = this.company) === null || _this$company === void 0 ? void 0 : _this$company.destroy(); } } } }); _this.build(data.lines); }, this.empty.bind(this)); }, onInstance: function onInstance(item, data) { item.use({ onMore: router.call.bind(router, 'category_full', { url: data.url, title: data.title, companies: object.company.id, sort_by: 'vote_count.desc', source: 'tmdb' }), onInstance: function onInstance(card, data) { card.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); } }); return comp; } /** * Компонент "Лента" * @param {*} object */ function Feed(object) { var network = new Request(); var scroll = new Scroll({ mask: true, over: true, step: 250, end_ratio: 2 }); var html = document.createElement('div'); var feed = []; var last; function enrichTmdbItem(element, oncomplite) { var card = element.data || {}; if (!card.imdb_id) return oncomplite(element); var method = 'find/' + card.imdb_id + '?external_source=imdb_id'; Api.sources.tmdb.get(method, {}, function (data) { var movie = data.movie_results && data.movie_results[0]; var tv = data.tv_results && data.tv_results[0]; var tmdb = element.card_type == 'tv' ? tv || movie : movie || tv; if (tmdb) { tmdb.source = 'tmdb'; tmdb.imdb_id = card.imdb_id; tmdb.imdb_rating = card.imdb_rating; tmdb.kp_rating = card.kp_rating; tmdb.release_quality = card.release_quality; tmdb.countries = card.countries; tmdb.episode = card.episode; element.data = tmdb; element.card_id = tmdb.id; element.card_type = tmdb.name || tmdb.first_air_date ? 'tv' : 'movie'; } oncomplite(element); }, function () { oncomplite(element); }, { life: 60 * 24 * 7 }); } function enrichTmdbFeed(items, oncomplite) { var result = []; var index = 0; var active = 0; var limit = 4; function next() { if (index >= items.length && active == 0) return oncomplite(result); var _loop = function _loop() { var position = index; var element = items[index]; index++; active++; enrichTmdbItem(element, function (updated) { result[position] = updated; active--; next(); }); }; while (active < limit && index < items.length) { _loop(); } } next(); } this.create = function () { var _this = this; this.activity.loader(true); network.silent(Utils$1.protocol() + object$2.cub_domain + '/api/feed/all', this.build.bind(this), function () { var empty = new Empty(); html.append(empty.render(true)); _this.start = empty.start.bind(empty); _this.activity.loader(false); _this.activity.toggle(); }); return this.render(); }; this.next = function () { if (object.page < 15) { object.page++; var offset = object.page - 1; this.append(feed.slice(20 * offset, 20 * offset + 20), true); } }; this.loadImg = function (box, src) { var img = box.find('img'); img.onload = function () { box.addClass('loaded'); }; img.onerror = function () { img.src = './img/img_broken.svg'; }; img.src = src; }; this.append = function (data, append) { var _this2 = this; data.forEach(function (element) { var item = Template.js(element.type == 'episode' || element.type == 'trailer' ? 'feed_episode' : 'feed_item'); item.addClass('feed-item--' + element.type); var type = { top: Lampa.Lang.translate('title_in_top'), now_playing: Lampa.Lang.translate('title_now_watch'), uhd: Lampa.Lang.translate('title_in_high_quality'), popular: Lampa.Lang.translate('title_popular'), trailer: Lampa.Lang.translate('title_trailers'), episode: Lampa.Lang.translate('card_new_episode'), now: Lampa.Lang.translate('title_new') }; var sity = element.data.countries || []; var year = ((element.data.release_date || element.data.first_air_date) + '').slice(0, 4); var info = []; var tags = []; info.push(year + (sity.length ? ' - ' + sity.slice(0, 2).join(', ') : '')); if (element.data.imdb_rating && parseFloat(element.data.imdb_rating) > 0) { info.push('IMDB ' + element.data.imdb_rating); } if (element.data.kp_rating && parseFloat(element.data.kp_rating) > 0) { info.push('KP ' + element.data.kp_rating); } if (element.type == 'episode') { tags = element.hash.split(';').map(function (a) { return Lang.translate(a.slice(0, 1) == 's' ? 'torrent_serial_season' : 'torrent_serial_episode') + ' - ' + a.slice(1); }); } else if (element.data.genres) tags.push(element.data.genres.join(', '));else if (element.data.genre_ids) { tags.push(Api.sources.tmdb.getGenresNameFromIds(element.card_type, element.data.genre_ids).join(', ')); } item.find('.feed-item__label').addClass('feed-item__label--' + element.type).text(type[element.type]); item.find('.feed-item__title').text(element.data.title || element.data.name); item.find('.feed-item__info').text(info.join(' / ')); item.find('.feed-item__descr').text(element.data.overview || ''); item.find('.feed-item__tags').text(tags.join(' / ')); _this2.loadImg(item.find('.feed-item__poster-box'), element.data.poster_path ? Api.img(element.data.poster_path, 'w500') : './img/img_broken.svg'); var image = item.find('.feed-item__image-box'); if (image) { _this2.loadImg(image, Api.img(element.type == 'episode' ? element.data.episode.still_path : element.data.backdrop_path, 'w780')); } scroll.append(item); var btn_watch = document.createElement('div'); btn_watch.addClass('simple-button selector'); btn_watch.text(Lang.translate('title_watch')); btn_watch.on('hover:focus', function () { last = btn_watch; scroll.update(item); Background.change(Api.img(element.data.poster_path, 'w500')); }); btn_watch.on('hover:enter', function () { Activity.push({ url: '', component: 'full', id: element.card_id, method: element.card_type, card: element.data, source: element.data.source || 'tmdb' }); }); item.find('.feed-item__buttons').append(btn_watch); if (append) Controller.collectionAppend(btn_watch); }); }; this.build = function (data) { var _this3 = this; enrichTmdbFeed(data.result || [], function (result) { feed = result; html.addClass('feed'); var head = Template.js('feed_head'); head.find('.feed-head__title').text(Lang.translate('lampa_movie_title')); head.find('.feed-head__info').html(Lang.translate('lampa_movie_descr')); head.on('hover:focus', scroll.update.bind(scroll, head)); scroll.minus(); scroll.onWheel = function (step) { Navigator.move(step > 0 ? 'down' : 'up'); }; scroll.onEnd = _this3.next.bind(_this3); scroll.append(head); _this3.append(feed.slice(0, 20)); html.append(scroll.render(true)); _this3.activity.loader(false); _this3.activity.toggle(); }); }; this.start = function () { Controller.add('content', { toggle: function toggle() { Controller.collectionSet(scroll.render(true)); Controller.collectionFocus(last || false, scroll.render(true)); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else Controller.toggle('menu'); }, right: function right() { Navigator.move('right'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('head'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down'); }, back: function back() { Activity.backward(); } }); Controller.toggle('content'); }; this.render = function () { return html; }; this.destroy = function () { network.clear(); scroll.destroy(); html.remove(); }; } /** * Компонент "Избранное" * @param {*} object * @returns */ function component$b(object) { var all = Favorite.all(); var comp = Utils$1.createInstance(Full$1, object, { empty: { router: 'bookmarks' } }); comp.use(Router, 0); comp.use({ onCreate: function onCreate() { var category = ['look', 'scheduled', 'book', 'like', 'wath', 'viewed', 'continued', 'thrown']; var lines = []; var folders = ['book', 'like', 'wath', 'viewed', 'scheduled', 'thrown']; var media = ['movies', 'tv']; var premium = Account$1.hasPremium(); var sync = Account$1.Permit.sync; lines.push({ results: [], params: { module: Helper$b.toggle(Helper$b.MASK.base, 'More', 'Event'), items: { view: 20 } } }); // Добавляем категории category.forEach(function (a) { if (all[a].length) { lines[0].results.push({ title: Lang.translate('title_' + a), count: all[a].length, limit: sync ? premium ? 2000 : 500 : 0, params: { module: Helper$8.only('Line', 'Callback'), createInstance: function createInstance(item) { return new Register(item); }, emit: { onEnter: router.call.bind(router, 'favorite', { type: a }) } } }); } }); if (lines[0].results.length == 0) lines = []; // Добавляем карточки category.forEach(function (a) { if (all[a].length) { var items = Arrays.clone(all[a].slice(0, 20)); items.forEach(function (item) { item.params = { emit: { onEnter: router.call.bind(router, 'full', item), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(item)); } } }; }); // Если есть папки, то добавляем их if (folders.indexOf(a) > -1) { var i = 0; media.forEach(function (m) { var filter = Utils$1.filterCardsByType(all[a], m); if (filter.length) { Arrays.insert(items, i, { results: filter, media: m, params: { module: Helper$9.only('Folder', 'Callback'), emit: { onEnter: router.call.bind(router, 'favorite', { title: Lang.translate('title_' + a) + ' - ' + Lang.translate('menu_' + m), type: a, filter: m }), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(filter[0])); } } } }); i++; } }); } lines.push({ title: Lang.translate('title_' + a), results: items, type: a, total_pages: all[a].length > 20 ? Math.ceil(all[a].length / 20) : 1, params: { module: Helper$b.toggle(Helper$b.MASK.base, 'Event'), emit: { onMore: router.call.bind(router, 'favorite', { type: a, page: 2 }) } } }); } }); ContentRows.call('bookmarks', {}, lines); if (lines.length) { comp.build(lines); } else comp.empty(); } }); return comp; } var Line = { onCreate: function onCreate() { this.html = Template.js('season_info', this.data); this.prefix = Template.prefix(this.html, 'season-info'); var head = []; if (this.data.vote_average) head.push(Lang.translate('title_rating') + ': ' + parseFloat(this.data.vote_average + '').toFixed(1)); if (this.data.air_date) head.push(Lang.translate('full_date_of_release') + ': ' + Utils$1.parseTime(this.data.air_date).full); if (this.data.episodes.length) head.push(Lang.translate('title_episodes') + ': ' + this.data.episodes.length); if (head.length) this.prefix.head.append(Arrays.flatMap(head.map(function (s) { return $('' + s + ''); }), function () { return $('  ●  '); }));else this.prefix.head.remove(); if (!this.html.overview) this.prefix.overview.remove(); } }; var SeasonMap = { Line: Line, Callback: Callback$1 }; var Season = /*#__PURE__*/function (_Constructor) { _inherits(Season, _Constructor); var _super = _createSuper(Season); function Season() { _classCallCheck(this, Season); return _super.apply(this, arguments); } return Season; }(Constructor(SeasonMap)); var Helper$2 = new Mask(Arrays.getKeys(SeasonMap)); Helper$2.MASK.base = Helper$2.MASK.none; function choiceSeason() { var total = this.object.seasons_count || Utils$1.countSeasons(this.object.card); var select = []; for (var i = total; i > 0; i--) { select.push({ title: Lang.translate('torrent_serial_season') + ' ' + i, season: i, selected: (this.object.season || Utils$1.countSeasons(this.object.card)) == i }); } Select.show({ title: Lang.translate('torrent_serial_season'), items: select, onSelect: function onSelect(a) { Controller.toggle('content'); Activity.replace({ season: a.season }); }, onBack: function onBack() { Controller.toggle('content'); } }); } /** * Компонент "Сезоны сериала" * @param {*} object */ function component$a(object) { var comp = Utils$1.createInstance(Full, object, { module: Helper$4.toggle(Helper$4.MASK.base, 'Explorer'), items: { mapping: 'list' } }); comp.use({ onCreate: function onCreate() { var _this = this; var season = object.season || Utils$1.countSeasons(object.card); Api.seasons(object.card, [season], function (v) { if (v[season] && v[season].episodes) { object.seasons_count = v[season].seasons_count || Utils$1.countSeasons(object.card); Arrays.extend(v[season], { params: { createInstance: function createInstance(item) { return new Season(item); }, module: Helper$2.only('Line', 'Callback'), emit: { onEnter: choiceSeason.bind(_this) } } }); var results = [v[season]]; v[season].episodes.forEach(function (episode) { // Передаем название сериала для таймкода episode.original_name = object.card.original_name; episode.params = { createInstance: function createInstance() { return new Episode(episode); }, module: Helper$a.toggle(Helper$a.MASK.base, 'Line') }; results.push(episode); }); if (!v[season].episodes.length) { Arrays.insert(results, 1, { episode_number: 1, season_number: season, air_date: '', name: Lang.translate('title_anons'), comeing: true, params: { createInstance: function createInstance(item) { return new Episode(item); }, module: Helper$a.only('Line') } }); } _this.build({ results: results }); } else { _this.empty(); } }); }, onController: function onController() { Background.immediately(Utils$1.cardImgBackgroundBlur(object.card)); } }); return comp; } /** * Компонент Персоны" * @param {*} object */ function component$9(object) { var comp = new Full$1(object); var next = null; comp.use({ onCreate: function onCreate() { var _this = this; var parts = [function (call) { Account$1.Api.persons(function (data) { if (!data.length) return call(); var persons = data.map(function (p) { return p.person; }); persons.forEach(function (person) { person.params = { module: Helper$9.only('Card', 'Release', 'Callback'), emit: { onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(person)); }, onEnter: router.call.bind(router, 'actor', person) } }; }); call({ title: Lang.translate('title_persons'), results: persons, params: { module: Helper$b.toggle(Helper$b.MASK.base, 'More', 'Event') } }); }, call); }]; function loadPart(partLoaded, partEmpty) { Api.partNext(parts, 3, partLoaded, partEmpty); } next = loadPart; TMDB.get('person/popular', {}, function (json) { if (!json.results) return loadPart(_this.build.bind(_this), _this.empty.bind(_this)); json.results.sort(function (a, b) { return a.popularity - b.popularity; }); var filtred = json.results.filter(function (p) { return p.known_for_department && p.known_for; }); var persons = filtred.filter(function (p) { return (p.known_for_department || '').toLowerCase() == 'acting' && p.known_for.length; }).slice(0, 10); persons.forEach(function (person_data, index) { var event = function event(call_inner) { TMDB.person({ only_credits: 'movie', id: person_data.id }, function (result) { if (!result.credits) return call_inner(); var cards = (result.credits.movie || []).filter(function (m) { return m.backdrop_path && m.vote_count > 20; }); cards.sort(function (a, b) { var da = a.release_date || a.first_air_date; var db = b.release_date || b.first_air_date; if (db > da) return 1;else if (db < da) return -1;else return 0; }); var src = person_data.profile_path ? TMDB.img(person_data.profile_path, 'w90_and_h90_face') : person_data.img || './img/actor.svg'; cards.forEach(function (item) { item.params = { emit: { onEnter: router.call.bind(router, 'full', item), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(item)); } } }; }); call_inner({ title: person_data.name, icon_img: src, results: cards.length > 5 ? cards.slice(0, 20) : [], params: { module: Helper$b.toggle(Helper$b.MASK.base, 'Icon', 'More', 'MoreFirst', 'Event'), text: Lang.translate('title_person_about'), emit: { onMore: router.call.bind(router, 'actor', person_data) } } }); }); }; parts.push(event); }); loadPart(_this.build.bind(_this), _this.empty.bind(_this)); }, function () { loadPart(_this.build.bind(_this), _this.empty.bind(_this)); }, { life: 60 * 24 * 3 }); }, onNext: function onNext(resolve, reject) { if (next) { next(resolve.bind(this), reject.bind(this)); } else reject.call(this); } }); return comp; } function facts(card_id, card_type, callback, error) { Api$1.load('ai/generate/facts/' + card_id + '/' + card_type, { timeout: 60000 }).then(callback)["catch"](error); } function recommendations(card_id, card_type, callback, error) { Api$1.load('ai/generate/recommend/' + card_id + '/' + card_type, { timeout: 60000 }).then(callback)["catch"](error); } function _search(query, callback, error) { Api$1.load('ai/search/' + encodeURIComponent(query), { timeout: 60000 }).then(callback)["catch"](error); } function discovery() { var source = { title: Lang.translate('title_ai_assistant'), search: function search(params, oncomplite) { source.params.nofound = Lang.translate('search_nofound'); _search(decodeURIComponent(params.query), function (json) { json.title = Lang.translate('title_ai_assistant'); json.results.forEach(function (element) { element.source = 'cub'; }); oncomplite(json.results.length ? [json] : []); }, function (e) { if (e.decode_code == 600) source.params.nofound = Lang.translate('ai_search_limit'); oncomplite([]); }); }, onCancel: function onCancel() { Api$1.clear(); }, params: { lazy: true, save: true, start_typing: Lang.translate('ai_search_start_typing') } }; return source; } var Ai$1 = { facts: facts, recommendations: recommendations, discovery: discovery }; var Ai = { onEmpty: function onEmpty(e) { var _this = this; var code = e.decode_code; var data = { title: Lang.translate('network_error'), descr: Lang.translate('subscribe_noinfo') }; if (code == 600) { data.title = Lang.translate('ai_subscribe_title'); data.descr = Lang.translate('ai_subscribe_descr'); data.noicon = true; data.width = 'medium'; data.buttons = [{ title: Lang.translate('account_premium_more'), onEnter: Modal$1.premium.bind(Modal$1) }]; } if (code == 347) { data.title = Lang.translate('empty_title_two'); data.descr = Lang.translate('empty_text_two'); } if (code == 345 || code == 403) { data.title = Lang.translate('account_none_title'); data.descr = Lang.translate('account_create'); data.noicon = true; data.buttons = [{ title: Lang.translate('settings_cub_signin_button'), onEnter: function onEnter() { Device.login(_this.start.bind(_this)); } }]; } this.empty_class = new Empty(data); this.scroll.append(this.empty_class.render(true)); this.start = this.empty_class.start.bind(this.empty_class); } }; /** * Компонент "Факты о фильме" * @param {*} object */ function component$8(object) { var comp = Utils$1.createInstance(Full, object, { module: Helper$4.only('Explorer', 'Loading'), loading: { icon: 'text' } }); comp.use(Ai, 2); comp.use({ onCreate: function onCreate() { var _this = this; var cache_name = ['facts', this.object.card.id, this.object.card.name ? 'tv' : 'movie'].join('_'); var cache_text = ''; Cache.getData('other', cache_name).then(function (text) { cache_text = text; })["finally"](function () { if (cache_text) { _this.build(cache_text); } else { Ai$1.facts(_this.object.card.id, _this.object.card.name ? 'tv' : 'movie', function (data) { Cache.rewriteData('other', cache_name, data.text)["catch"](function () {}); _this.build(data.text); }, _this.empty.bind(_this)); } }); }, onBuild: function onBuild(text) { var _this2 = this; try { this.body.html(Utils$1.simpleMarkdownParser(text)); this.body.find('h1').remove(); this.body.addClass('text-markdow explorer-list animate-up-content animate-opacity'); this.scroll.onWheel = function (step) { if (!Controller.own(_this2)) _this2.start(); if (step > 0) Controller.move('down');else Controller.move('up'); }; } catch (e) { this.empty({ status: 245, message: e.message }); } }, onController: function onController(controller) { var _this3 = this; controller.up = function () { if (_this3.scroll.position() == 0) Controller.toggle('head');else _this3.scroll.wheel(-150); }; controller.down = function () { _this3.scroll.wheel(150); }; } }); return comp; } /** * Компонент "Рекомендации по фильму/сериалу" * @param {*} object */ function component$7(object) { var comp = Utils$1.createInstance(Full, object, { module: Helper$4.toggle(Helper$4.MASK.base, 'Explorer', 'Loading', 'Next', 'Empty'), items: { cols: 5 } }); comp.use(Ai, 2); comp.use({ onCreate: function onCreate() { var _this = this; Ai$1.recommendations(object.card.id, object.card.name ? 'tv' : 'movie', function (data) { if (data.results.length == 0) _this.empty({ status: 347 });else _this.build(data); }, this.empty.bind(this)); }, onInstance: function onInstance(item, data) { item.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); return comp; } /** * Компонент "Рекомендации" * @param {*} object */ function component$6(object) { var comp = Utils$1.createInstance(Full, object, { module: Helper$4.only('Explorer', 'Next', 'Empty', 'Items'), items: { mapping: 'list' } }); comp.use({ onCreate: function onCreate() { var _this = this; CUB.discussGet(object, function (data) { _this.build(_objectSpread2(_objectSpread2({}, data), {}, { results: data.result || [] })); }, this.empty.bind(this)); }, onNext: function onNext(resolve, reject) { CUB.discussGet(object, function (data) { resolve(_objectSpread2(_objectSpread2({}, data), {}, { results: data.result || [] })); }, reject); }, onCreateAndAppend: function onCreateAndAppend(element) { var item = Utils$1.createInstance(Discuss, element, { module: Helper$5.only('Line', 'Read'), line: { full_text: true } }); this.emit('instance', item, element); item.create(); this.emit('append', item, element); } }); return comp; } var data = []; /** * Запуск */ function init$w() { data = Storage.cache('recomends_scan', 300, []); Timer.add(120 * 1000, function () { var history = Favorite.get({ type: 'history' }); var added = 0; console.log('Recomendations', 'find history:', history.length); history.forEach(function (elem) { if (['cub', 'tmdb'].indexOf(elem.source) >= 0) { var id = data.filter(function (a) { return a.id == elem.id; }); if (!id.length) { data.push({ id: elem.id, tv: elem.number_of_seasons || elem.seasons }); added++; } } }); console.log('Recomendations', 'added to scan:', added, 'ready:', data.length); Storage.set('recomends_scan', data); search(); }); ContentRows.add({ name: 'recomend_watch', title: Lang.translate('title_recomend_watch'), index: 1, screen: ['main', 'category'], call: function call(params, screen) { if (params.url == 'anime') return; var media = screen == 'main' ? 'movie' : params.url; var all = get$4(media); var results = Arrays.shuffle(all).slice(0, 20); var total_pages = Math.ceil(all.length / 20); if (!results.length) return; return function (call) { call({ results: results, title: Lang.translate('title_recomend_watch'), total_pages: total_pages, page: 1, params: { emit: { onlyMore: function onlyMore() { router.call('recomend', { media: media, total_pages: total_pages }); } } } }); }; } }); } function search() { var ids = data.filter(function (e) { return !e.scan; }); if (ids.length) { var elem = ids[0]; elem.scan = 1; console.log('Recomendations', 'scan:', elem.id, elem.title || elem.name); TMDB.get((elem.tv ? 'tv' : 'movie') + '/' + elem.id + '/recommendations', {}, function (json) { console.log('Recomendations', 'result:', json.results && json.results.length ? json.results.length : 0); if (json.results && json.results.length) { var recomend = Storage.cache('recomends_list', 100, []); var favorite = Favorite.get({ type: 'history' }); json.results.forEach(function (e) { var year = (e.first_air_date || e.release_date || '0000').split('-'); if (!recomend.filter(function (r) { return r.id == e.id; }).length && !favorite.filter(function (h) { return h.id == e.id; }).length && year[0] > new Date().getFullYear() - 20) { recomend.push(e); } }); Storage.set('recomends_list', recomend); } }); } else { data.forEach(function (a) { return a.scan = 0; }); } Storage.set('recomends_scan', data); } function get$4(type) { var all = Storage.get('recomends_list', '[]'); var items = all.filter(function (e) { return type == 'tv' ? e.number_of_seasons || e.first_air_date : !(e.number_of_seasons || e.first_air_date); }).reverse(); var history = Favorite.get({ type: 'history' }); items = items.filter(function (e) { return !history.find(function (h) { return h.id == e.id; }); }); return items; } function page(object, call, empty) { var all = get$4(object.media); var page = object.page || 1; var result = all.slice((page - 1) * 20, page * 20); if (result.length == 0) return empty({ status: 404 }); call({ results: result, title: Lang.translate('title_recomend_watch'), total_pages: Math.ceil(all.length / 20), page: page }); } var Recomends = { init: init$w, get: get$4, page: page }; /** * Компонент "Рекомендации по фильму/сериалу" * @param {*} object */ function component$5(object) { var comp = Utils$1.createInstance(Full, object); comp.use({ onCreate: function onCreate() { Recomends.page(object, this.build.bind(this), this.empty.bind(this)); }, onNext: function onNext(resolve, reject) { Recomends.page(object, resolve.bind(this), reject.bind(this)); }, onInstance: function onInstance(item, data) { item.use({ onEnter: router.call.bind(router, 'full', data), onFocus: function onFocus() { Background.change(Utils$1.cardImgBackground(data)); } }); } }); return comp; } var component$4 = { main: component$n, full: component$m, category: component$k, category_full: component$l, actor: component$j, favorite: component$i, torrents: component$h, mytorrents: component$g, relise: component$f, nocomponent: Component$1, timetable: component$e, subscribes: component$d, company: component$c, feed: Feed, bookmarks: component$b, episodes: component$a, myperson: component$9, ai_facts: component$8, ai_recommendations: component$7, discuss: component$6, recomend: component$5 }; /** * Создать компонент * @param {{component:string}} object * @returns */ function create$5(object) { if (component$4[object.component]) { try { return new component$4[object.component](object); } catch (e) { console.log('Component', 'create error', e.stack); return new component$4.nocomponent(object); } } else { return new component$4.nocomponent(object); } } /** * Добавить компонент * @param {string} name * @param {class} comp */ function add$5(name, comp) { component$4[name] = comp; } /** * Получить компонент * @param {string} name * @returns {class} */ function get$3(name) { return component$4[name]; } var Component = { create: create$5, add: add$5, get: get$3 }; function close$3() { if (Platform.is('apple_tv')) window.location.assign('exit://exit'); if (Platform.is('tizen')) tizen.application.getCurrentApplication().exit(); if (Platform.is('webos') && typeof window.close == 'function') window.close(); if (Platform.is('android')) Android.exit(); if (Platform.is('orsay')) Orsay.exit(); if (Platform.is('netcast')) window.NetCastBack(); if (Platform.is('noname')) window.history.back(); } function loadStyle() { /** Start - для orsay одни стили, для других другие */ var old_css = $('link[href="css/app.css"]'); var urlStyle = 'http://lampa.mx/css/app.css?v'; if (Platform.is('orsay') && window.location.host.indexOf('localhost') == -1) { //Для нового типа виджета берем сохраненный адрес загрузки if (Orsay.isNewWidget()) { //Для фрейм загрузчика запишем полный url if (location.protocol != 'file:') { var newloaderUrl = location.href.replace(/[^/]*$/, ''); if (newloaderUrl.slice(-1) == '/') { newloaderUrl = newloaderUrl.substring(0, newloaderUrl.length - 1); } if (Orsay.getLoaderUrl() != newloaderUrl) { Orsay.setLoaderUrl(newloaderUrl); } } //console.log('Loader', 'start url: ', Orsay.getLoaderUrl()); urlStyle = Orsay.getLoaderUrl() + '/css/app.css?v'; } Utils$1.putStyle([urlStyle + object$2.css_version], function () { old_css.remove(); }); } else if (window.lampa_settings.fix_widget || old_css.length) { LoadingProgress.status('PutStyle ' + object$2.css_version); Utils$1.putStyle([object$2.github_lampa + 'css/app.css?v' + object$2.css_version], function () { old_css.remove(); }, function () { // Что-то не грузится, пробуем второй вариант Utils$1.putStyle([urlStyle + object$2.css_version], function () { old_css.remove(); }); }); } } function modalClose() { var controller = Controller.enabled().name; Modal.open({ title: '', align: 'center', zIndex: 300, html: $('
    ' + Lang.translate('close_app_modal') + '
    '), buttons: [{ name: Lang.translate('settings_param_no'), onSelect: function onSelect() { Modal.close(); Controller.toggle(controller); } }, { name: Lang.translate('settings_param_yes'), onSelect: function onSelect() { Modal.close(); Controller.toggle(controller); close$3(); } }] }); } var App = { close: close$3, loadStyle: loadStyle, modalClose: modalClose }; /** * Универсальный провайдер пропсов * @param data - начальные данные * @example let props = new Props({a:1, b:2}) props.set('c', 3) props.set({a:10, d:4}) console.log(props.get('a')) // 10 console.log(props.get('b')) // 2 console.log(props.get('c')) // 3 console.log(props.get('d')) // 4 console.log(props.get('e', 5)) // 5 console.log(props.pick('a','b','e')) // {a:10, b:2, e:undefined} console.log(props.all()) // {a:10, b:2, c:3, d:4} */ var Props = function Props(data) { var _this = this; _classCallCheck(this, Props); _defineProperty(this, "set", function (key, value) { if (Arrays.isObject(key)) { for (var k in key) { _this.data[k] = key[k]; } return; } _this.data[key] = value; }); _defineProperty(this, "get", function (key, def) { return _this.data[key] || def; }); _defineProperty(this, "pick", function () { for (var _len = arguments.length, keys = new Array(_len), _key = 0; _key < _len; _key++) { keys[_key] = arguments[_key]; } return Object.fromEntries(keys.map(function (k) { return [k, _this.data[k]]; })); }); _defineProperty(this, "all", function () { return _this.data; }); this.data = data || {}; }; /** * Слайд активности * @param {Object} component - Компонент активности * @param {Object} object - Параметры активности */ var ActivitySlide = /*#__PURE__*/function () { function ActivitySlide(component, object) { _classCallCheck(this, ActivitySlide); this.component = component; this.object = object || {}; this.slide = Template.js('activity'); this.body = this.slide.find('.activity__body'); this.is_stopped = false; this.is_started = false; } /** * Создание активности * @returns {void} */ _createClass(ActivitySlide, [{ key: "create", value: function create() { try { this.component.create(this.body); this.body.append(this.component.render(true)); } catch (e) { console.log('Activity', 'create error:', e.stack); Noty.show('Activity create error:' + e.message + ' ' + e.stack); this.component = Component.create({ component: 'nocomponent' }); this.component.activity = this; this.create(); } } /** * Показывает загрузку * @param {boolean} status */ }, { key: "loader", value: function loader(status) { this.slide.toggleClass('activity--load', status); } /** * Стартуем активную активность * @return {void} */ }, { key: "start", value: function start() { this.is_started = true; this.is_stopped = false; Controller.add('content', { invisible: true, toggle: function toggle() { Controller.clear(); }, left: function left() { Controller.toggle('menu'); }, up: function up() { Controller.toggle('head'); }, back: function back() { Activity.backward(); } }); Controller.toggle('content'); if (this.waite_refresh) this.runRefresh();else { try { this.component.start(); } catch (e) { console.log('Activity', 'start error:', e.stack); Noty.show('Activity start error:' + e.message + ' ' + e.stack); } } } /** * Пауза активности * @return {void} */ }, { key: "pause", value: function pause() { this.is_started = false; this.component.pause && this.component.pause(); } /** * Включаем активность если она активна * @return {void} */ }, { key: "toggle", value: function toggle() { if (this.is_started) this.start(); } /** * Событие изменения размеров окна * @return {void} */ }, { key: "resize", value: function resize() { if (this.component.resize) this.component.resize(); } /** * Обновить активность (если активна) или при следующем старте * @return {void} */ }, { key: "refresh", value: function refresh() { if (this.waite_refresh) return; this.waite_refresh = true; if (Activity.own(this.component)) this.runRefresh(); } /** * Запуск обновления активности * @returns {void} */ }, { key: "runRefresh", value: function runRefresh() { var _this = this; // Если нет пометки на обновление, то не обновляем if (!this.waite_refresh) return; clearTimeout(this.timer_refresh); // При переходе назад текушая активность уничтожается через 200мс, поэтому ждем на удаление и запускаем обновление this.timer_refresh = setTimeout(function () { if (_this.component.beforeRefresh) _this.component.beforeRefresh(); Activity.replace(); }, 400); this.loader(true); } }, { key: "canRefresh", value: function canRefresh() { console.warn('activity.canRefresh is deprecated'); return false; } }, { key: "needRefresh", value: function needRefresh() { console.warn('activity.needRefresh is deprecated, use activity.refresh()'); } /** * Останавливает активность когда открывается другая * @return {void} */ }, { key: "stop", value: function stop() { this.is_started = false; if (this.is_stopped) return; this.is_stopped = true; this.component.stop && this.component.stop(); this.slide.remove(); } /** * Вернуть HTML слайд активности * @param {boolean} js - вернуть js объект * @returns {jQuery|HTMLElement} - HTML слайд активности */ }, { key: "render", value: function render(js) { return js ? this.slide : $(this.slide); } /** * Уничтожение активности * @returns {void} */ }, { key: "destroy", value: function destroy() { try { this.component.destroy(); } catch (e) { console.log('Activity', 'destroy error:', e.stack); } // После create работает долгий запрос и затем вызывается build, однако уже было вызвано destroy и возникают ошибки, поэтому заодно чистим функцию build и остальные функции компонента for (var f in this.component) { if (typeof this.component[f] == 'function') { this.component[f] = function () {}; } } // Помечаем что компонент уничтожен, для внутренних функций компонента this.component.destroyed = true; this.slide.remove(); clearTimeout(this.timer_refresh); } }]); return ActivitySlide; }(); var listener$3 = Subscribe$2(); var activites = []; var callback = false; var fullout = false; var focustime = Date.now(); var content; var slides; var maxsave; var base; var refresh_timer; function parseStart() { if (window.start_deep_link) return; var id = Utils$1.gup('card'); if (id) { window.start_deep_link = { id: id, component: "full", method: Utils$1.gup('media') || 'movie', source: Utils$1.gup('source') || 'cub', card: { id: id, source: Utils$1.gup('source') || 'cub' } }; } else if (!(Platform.is('orsay') || Platform.is('netcast') || Platform.is('webos') || Platform.is('tizen'))) { try { var params = new URLSearchParams(window.location.search); if (params.has('component')) { window.start_deep_link = {}; params.forEach(function (v, n) { window.start_deep_link[n] = v; }); } } catch (e) { console.log('Activity', 'url params start error:', e.message); } } } /** * Запуск */ function init$v() { content = Template.js('activitys'); slides = content.querySelector('.activitys__slides'); maxsave = Storage.get('pages_save_total', 5); base = document.querySelector('head base'); parseStart(); empty(); var wait = true; var swip_status = 0; var swip_timer; var start_time = Date.now(); setTimeout(function () { wait = false; }, 1500); setTimeout(last, 500); window.addEventListener('popstate', function () { if (fullout || wait) return; Screensaver$2.stop(); if (swip_status == 0) empty(); //это чтоб не выходило с приложения, однако на айфонах это вызвает зависание на 2-3 сек listener$3.send('popstate', { count: activites.length }); if (callback) callback();else { backward(); } }); Storage.listener.follow('change', function (event) { if (event.name == 'pages_save_total') maxsave = Storage.get('pages_save_total', 5); if (event.name == 'light_version' || event.name == 'account_use' || event.name == 'interface_size' || event.name == 'account' && !event.value) refresh(true); }); // Обновляем активность при изменении профиля, протокола или прочитаных ссылок в закладках и истории Lampa.Listener.follow('state:changed', function (e) { if (e.target == 'favorite' && (e.reason == 'profile' || e.reason == 'read')) refresh(true); }); // После имземения размера окна возникает поломанный интерфейс, надо перезапустить активность или уведомить компонент о изменении Lampa.Listener.follow('resize_end', function (e) { activites.forEach(function (activity) { if (activity.activity) activity.activity.resize(); }); }); // Исключительно для огрызков пришлось мутить работу свайпа назад if (Platform.is('apple')) { var body = document.querySelector('body'); body.addEventListener('touchstart', function (e) { var point = e.touches[0] || e.changedTouches[0]; if (point.clientX < window.innerWidth * 0.15 && point.clientX < window.innerHeight - 120) { swip_status = 1; clearTimeout(swip_timer); swip_timer = setTimeout(function () { swip_status = 0; }, 2000); } else { swip_status = 0; } }); } // Выход из приложения listener$3.follow('backward', function (event) { var noout = Platform.is('browser') || Platform.desktop(); if (event.count == 1 && Date.now() > start_time + 1000 * 2 && !noout) { var enabled = Controller.enabled().name; Select.show({ title: Lang.translate('title_out'), items: [{ title: Lang.translate('title_out_confirm'), out: true }, { title: Lang.translate('cancel') }], onSelect: function onSelect(a) { if (a.out) { out(); Controller.toggle(enabled); App.close(); } else { Controller.toggle(enabled); } }, onBack: function onBack() { Controller.toggle(enabled); } }); } }); // Обновляем активность при уходе и возвращении на страницу document.addEventListener('visibilitychange', resetFocusTime); // Дублирование для Android, где visibilitychange может не сработать Timer.add(1000, resetFocusTime, false, true); PlayerVideo.listener.follow('timeupdate', resetFocusTime); Keypad.listener.follow('keydown', resetFocusTime); } /** * Сбросить время фокуса */ function resetFocusTime() { if (Date.now() - focustime > 1000 * 60 * 60 * 6) refresh(true); focustime = Date.now(); } /** * Обновить активность или все активности * @param {boolean} all - обновить все активности * @return {void} */ function refresh() { var all = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; clearTimeout(refresh_timer); // Обновляем активность через 1 секунду, чтобы не было сразу нескольких обновлений refresh_timer = setTimeout(function () { if (all) { activites.forEach(function (activity) { if (activity.activity) activity.activity.refresh(); }); } else { var curent = active$3(); if (curent && curent.activity) curent.activity.refresh(); } }, 1000); } /** * Лимит активностей, уничтожать если больше maxsave * @return {void} */ function limit() { var curent = active$3(); if (curent && curent.activity) curent.activity.pause(); var tree_stop = activites.slice(-2); if (tree_stop.length > 1 && tree_stop[0].activity) tree_stop[0].activity.stop(); var tree_destroy = activites.slice(-maxsave); if (tree_destroy.length > maxsave - 1) { var first = tree_destroy[0]; if (first.activity) { first.activity.destroy(); Lampa.Listener.send('activity', { component: first.component, type: 'destroy', object: first }); first.activity = null; } } } /** * Обновить адрес в строке из активности * @param {object} object - параметры активности * @param {boolean} replace - заменить текущий адрес, по умолчанию false (добавить в историю) * @param {string} mix - дополнительные параметры в строку * @returns {void} */ function pushState(object, replace, mix) { var path = window.location.protocol == 'file:' ? '' : base ? '/' : ''; if (!window.lampa_settings.push_state) return window.history.pushState(null, null, path); var data = Arrays.clone(extractObject(object)); var comp = []; for (var n in data) { if ((typeof data[n] == 'string' || typeof data[n] == 'number' || typeof data[n] == 'boolean') && data[n]) comp.push(n + '=' + encodeURIComponent(data[n])); } var card = object.card || object.movie; var meth = object.method || (card ? card.name ? 'tv' : 'movie' : ''); var sour = object.source || (card ? card.source : 'tmdb'); var durl = card && card.id ? '?card=' + card.id + (meth ? '&media=' + meth : '') + (sour ? '&source=' + sour : '') : '?' + comp.join('&'); if (mix) durl += '&' + mix; if (replace) window.history.replaceState(null, null, path + durl);else window.history.pushState(null, null, path + durl); } /** * Обновить адрес в строке из активности с добавлением дополнительных параметров * @param {string} mix - дополнительные параметры в строку * @returns {void} */ function mixState(mix) { var curent = active$3(); if (curent && curent.activity) pushState(curent, true, mix); } /** * Добавить новую активность * @param {{component:string}} object - параметры активности * @returns {void} */ function push$3(object) { limit(); create$4(object); activites.push(object); start$4(object); pushState(object); } /** * Создать новую активность * @param {{component:string}} object - параметры активности * @returns {void} */ function create$4(object) { var comp = Component.create(object); comp.props = new Props(Arrays.clone(extractObject(object))); object.activity = new ActivitySlide(comp, object); comp.activity = object.activity; slides.append(object.activity.render(true)); Lampa.Listener.send('activity', { component: object.component, type: 'init', object: object }); object.activity.create(); Lampa.Listener.send('activity', { component: object.component, type: 'create', object: object }); } /** * Вызов (назад) пользователем * @return {void} */ function back$3() { listener$3.send('popstate', { count: activites.length }); if (callback) callback();else { backward(); } } /** * Получить активную активность * @returns {object} * @return {{component:string, activity:class}} - параметры активности */ function active$3() { return activites[activites.length - 1]; } function inActivity() { return $('body').hasClass('settings--open') || $('body').hasClass('menu--open') ? false : true; } /** * Создать пустую историю * @return {void} */ function empty() { var curent = active$3(); if (curent && curent.activity) pushState(curent, false, 'r=' + Math.random()); } /** * Получить все активности * @returns {[{component:string, activity:class}]} */ function all() { return activites; } /** * Получить рендеры всех активностей * @param {boolean} js - вернуть js объекты * @returns {array} */ function renderLayers(js) { var result = []; all().forEach(function (item) { if (item.activity) result.push(item.activity.render(js)); }); return result; } /** * Обработать событие назад * @return {void} */ function backward() { callback = false; listener$3.send('backward', { count: activites.length }); if (activites.length == 1) return; Array.from(slides.children).forEach(function (slide) { return slide.classList.remove('activity--active'); }); var curent = activites.pop(); if (curent) { setTimeout(function () { curent.activity.destroy(); Lampa.Listener.send('activity', { component: curent.component, type: 'destroy', object: curent }); }, 200); } var previous_tree = activites.slice(-maxsave); if (previous_tree.length > maxsave - 1) { create$4(previous_tree[0]); } previous_tree = activites.slice(-1)[0]; if (previous_tree) { if (previous_tree.activity) { start$4(previous_tree); Lampa.Listener.send('activity', { component: previous_tree.component, type: 'archive', object: previous_tree }); pushState(previous_tree, true); } else { create$4(previous_tree); start$4(previous_tree); pushState(previous_tree); } } } /** * Сохранить активность в память * @param {{component:string, activity:class}} object * @return {void} */ function save(object) { Storage.set('activity', extractObject(object)); } /** * Получить данные активности * @param {{component:string, activity:class}} object - параметры активности * @returns {{component:string}} */ function extractObject(object) { var saved = {}; for (var i in object) { if (!(i == 'activity' || i == 'props' || i == 'params')) saved[i] = object[i]; } return saved; } /** * Активируем следующию активность * @param {{component:string, activity:class}} object - параметры активности * @return {void} */ function start$4(object) { Head.title(object.title); if (object.activity.is_stopped) { slides.append(object.activity.render(true)); } object.activity.start(); save(object); Array.from(slides.children).forEach(function (slide) { slide.hasClass('activity--active') && slide.removeClass('activity--active'); }); object.activity.render().addClass('activity--active'); Lampa.Listener.send('activity', { component: object.component, type: 'start', object: object }); } /** * С какой активности начать запуск лампы * @return {void} */ function last() { var active = Storage.get('activity', 'false'); var start_from = Storage.field("start_page"); if (window.lampa_settings.iptv) { active = { component: 'iptv', page: 1 }; push$3(active); } else if (window.start_deep_link) { window.start_deep_link.page = 1; push$3(window.start_deep_link); } else if (active && start_from === "last") { if (active.page) active.page = 1; push$3(active); } else { var _start_from$split = start_from.split('@'), _start_from$split2 = _slicedToArray(_start_from$split, 2), action = _start_from$split2[0], type = _start_from$split2[1]; if (action == 'favorite') { push$3({ url: '', title: Lang.translate(type == 'bookmarks' ? 'settings_input_links' : 'title_history'), component: type == 'bookmarks' ? 'bookmarks' : 'favorite', type: type, page: 1 }); } else if (action == 'mytorrents') { push$3({ url: '', title: Lang.translate('title_mytorrents'), component: 'mytorrents', page: 1 }); } else { push$3({ url: '', title: Lang.translate('title_main') + ' - ' + Storage.field('source').toUpperCase(), component: 'main', source: Storage.field('source'), page: 1 }); } } } /** * Рендер * @returns {HTMLElement} */ function render$2() { return content; } /** * Подключить обратный вызов при изменени истории * @param {function} call - функция обратного вызова * @return {void} */ function call(call) { callback = call; } /** * Выход из лампы * @return {void} */ function out() { fullout = true; back$3(); for (var i = 0; i < window.history.length; i++) { back$3(); } setTimeout(function () { fullout = false; empty(); }, 100); } /** * Заменить активную активность * @param {object} replace - заменить на новые параметры * @param {boolean} clear - использовать только новые параметры, по умолчанию false * @return {void} */ function replace() { var replace = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var clear = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var object = extractObject(active$3()); for (var i in replace) { object[i] = replace[i]; } var made = active$3(); made.activity.destroy(); Lampa.Listener.send('activity', { component: made.component, type: 'destroy', object: made }); activites.pop(); push$3(clear ? replace : object); } /** * Проверить что активность принадлежит компоненту * @param {object} component - компонент * @returns {boolean} */ function own$1(component) { var curent = active$3(); return curent && curent.activity && curent.activity === component.activity; } var Activity = { init: init$v, listener: listener$3, push: push$3, back: back$3, render: render$2, backward: backward, call: call, last: last, out: out, replace: replace, active: active$3, all: all, extractObject: extractObject, renderLayers: renderLayers, inActivity: inActivity, pushState: pushState, mixState: mixState, own: own$1, refresh: refresh }; var listener$2 = Subscribe$2(); var active$2; var active_name = ''; var controlls = {}; var select_active; var try_close = false; function observe() { if (typeof MutationObserver == 'undefined') return; var observer = new MutationObserver(function (mutations) { for (var i = 0; i < mutations.length; i++) { var mutation = mutations[i]; if (mutation.type == 'childList' && !mutation.removedNodes.length) { var selectors = Array.from(mutation.target.querySelectorAll('.selector')); selectors.forEach(function (elem) { if (!elem.classList.contains('hg-button')) bindEvents(elem); }); } } }); observer.observe(document, { childList: true, subtree: true }); } function animateTriggerEnter(elem) { if (Storage.field('advanced_animation')) { elem.addClass('animate-trigger-enter'); setTimeout(function () { elem.removeClass('animate-trigger-enter'); }, 500); } } /** * Добавить контроллер * @param {String} name * @param {Object} calls */ function add$4(name, calls) { controlls[name] = calls; } /** * Запустить функцию * @param {String} name * @param {Object} params */ function run(name, params) { if (active$2) { if (active$2[name]) { if (typeof active$2[name] == 'function') active$2[name](params);else if (typeof active$2[name] == 'string') { run(active$2[name], params); } } } } /** * Двигать * @param {String} direction */ function move(direction) { run(direction); } /** * Вызов enter */ function enter() { if (active$2 && active$2.enter) run('enter');else if (select_active) { animateTriggerEnter(select_active); Utils$1.trigger(select_active, 'hover:enter'); } } /** * Вызов long */ function _long() { if (active$2 && active$2["long"]) run('long');else if (select_active) Utils$1.trigger(select_active, 'hover:long'); } /** * Завершить */ function finish() { run('finish'); } /** * Нажали назад */ function back$2() { run('back'); } /** * Переключить контроллер * @param {String} name */ function toggle$2(name) { if (active$2 && active$2.gone) active$2.gone(name); if (controlls[name]) { if (name == 'content') toContent(); active$2 = controlls[name]; active_name = name; Activity.call(function () { run('back'); }); if (active$2.toggle) active$2.toggle(); if (active$2.update) active$2.update();else { Layer.update(); } listener$2.send('toggle', { name: name }); } } function bindEvents(elem) { if (elem.classList.contains('selector') && !elem.bind_events) { elem.bind_events = true; var long_position = 0; var long_timer; var longStart = function longStart() { clearTimeout(long_timer); var offset = elem.getBoundingClientRect(); long_timer = setTimeout(function () { var time = elem.long_time || 0; offset = elem.getBoundingClientRect(); if (time + 100 < Date.now()) { var mutation = Math.abs(long_position - (offset.top + offset.left)); if (mutation < 30) Utils$1.trigger(elem, 'hover:long'); } elem.long_time = Date.now(); }, 800); long_position = offset.top + offset.left; }; var longClear = function longClear() { clearTimeout(long_timer); }; var touchStart = function touchStart() { longStart(); Utils$1.trigger(elem, 'hover:touch'); }; var rightClick = function rightClick(e) { Utils$1.trigger(elem, 'hover:long'); }; elem.trigger_click = function (e) { if (DeviceInput.canClick(e) && DeviceInput.noDubleClick(e)) { animateTriggerEnter(elem); Utils$1.trigger(elem, 'hover:enter'); } }; elem.trigger_mouseenter = function () { clearAllFocus(); elem.toggleClass('focus', true); Utils$1.trigger(elem, 'hover:hover'); }; elem.trigger_mouseleave = function () { elem.toggleClass('focus', false); }; elem.addEventListener('click', function (e) { setTimeout(elem.trigger_click.bind(null, e), 20); }); if (!Utils$1.isTouchDevice() && Storage.field('navigation_type') == 'mouse') { elem.addEventListener('mouseenter', elem.trigger_mouseenter); elem.addEventListener('mouseleave', elem.trigger_mouseleave); elem.addEventListener('mouseout', longClear); elem.addEventListener('mouseup', longClear); elem.addEventListener('mousedown', longStart); elem.addEventListener('contextmenu', rightClick); } if (Utils$1.isTouchDevice()) { elem.addEventListener('touchstart', touchStart); elem.addEventListener('touchend', longClear); elem.addEventListener('touchmove', longClear); } } } function enable(name) { if (active_name == name) toggle$2(name); } function clearSelects() { select_active = false; removeClass(['focus']); } function clearAllFocus() { var collection = Array.from(document.body.querySelectorAll('.selector')); collection.forEach(function (item) { return item.toggleClass('focus', false); }); } /** * Вызвать событие * @param {String} name * @param {Object} params */ function trigger$1(name, params) { run(name, params); } /** * Очистить классы * @param {Array} classes */ function removeClass(classes) { if (Navigator._collection) { Navigator._collection.forEach(function (element) { classes.forEach(function (class_name) { element.toggleClass(class_name, false); }); }); } } /** * Фокус на элементе * @param {Object} target */ function focus(target) { Utils$1.trigger(target, 'hover:focus'); if (Platform.screen('tv')) { removeClass(['focus']); target.toggleClass('focus', true); } select_active = target; } function collectionSet(html) { var append = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var visible_only = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; html = html instanceof jQuery ? html[0] : html; append = append instanceof jQuery ? append[0] : append; var colection = Array.from(html.querySelectorAll('.selector')); if (visible_only) colection = colection.filter(function (e) { return e.offsetParent !== null; }); if (append) { colection = colection.concat(Array.from(append.querySelectorAll('.selector'))); } if (colection.length || active$2.invisible) { clearSelects(); Navigator.setCollection(colection); } } function collectionAppend(append) { append = append instanceof jQuery ? append.toArray() : append; if (!append.length) append = Array.from([append]); Navigator.multiAdd(append); } function collectionFocus(target, html) { var visible_only = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; html = html instanceof jQuery ? html[0] : html; target = target instanceof jQuery ? target[0] : target; if (target && target.offsetParent === null) target = false; if (target) { Navigator.focus(target); } else { var colection = Array.from(html.querySelectorAll('.selector')).filter(function (elem) { return !elem.classList.contains('hide'); }); if (visible_only) colection = colection.filter(function (e) { return e.offsetParent !== null; }); if (colection.length) Navigator.focus(colection[0]); } } function own(link) { return active$2 && active$2.link == link; } function enabled() { return { name: active_name, controller: active$2 }; } function toContent() { if (try_close) return; try_close = true; var trys = 0; Screensaver$2.stopSlideshow(); var any = function any() { return parseInt([$('body').hasClass('settings--open') ? 1 : 0, $('body').hasClass('selectbox--open') ? 1 : 0, $('.modal,.youtube-player,.player,.search-box,.search').length ? 1 : 0].join('')); }; var close = function close() { var contrl = enabled(); trys++; if (any()) { try { if (contrl.controller.back) contrl.controller.back(); } catch (e) {} if (trys < 10) close(); } }; var remove = function remove() { try { if ($('body').hasClass('settings--open')) $('body').removeClass('settings--open'); if ($('body').hasClass('selectbox--open')) Select.close(); if ($('.modal').length) Modal.close(); if ($('.player').length) Player.close(); if ($('.search-box').length) $('.search-box').remove(); if ($('body').hasClass('search--open')) Search.close(); $('body').removeClass('ambience--enable'); } catch (e) {} }; close(); remove(); try_close = false; } function clear$2() { clearSelects(); Navigator.setCollection([]); } var Controller = { listener: listener$2, observe: observe, add: add$4, move: move, enter: enter, finish: finish, toggle: toggle$2, trigger: trigger$1, back: back$2, focus: focus, collectionSet: collectionSet, collectionFocus: collectionFocus, collectionAppend: collectionAppend, enable: enable, enabled: enabled, "long": _long, toContent: toContent, updateSelects: function updateSelects() {}, own: own, clear: clear$2 }; var http = window.location.protocol == 'https:' ? 'https://' : 'http://'; var layers = { search: { 'sim': ['{MIC} {ABC} 1 2 3 4 5 6 7 8 9 0 {BKSP}', '{LANG} - + _ : ( ) [ ] . / {SPACE}'], 'en': ['{MIC} q w e r t y u i o p {BKSP}', '{LANG} a s d f g h j k l', '{SIM} z x c v b n m . {SPACE}'], 'uk': ['{MIC} й ц у к е н г ш щ з х ї {BKSP}', '{LANG} ф і в а п р о л д ж є', '{SIM} я ч с м и т ь б ю . {SPACE}'], 'he': ['{MIC} ק ר א ט ו ן ם פ {BKSP}', '{LANG} ש ד ג כ ע י ח ל ך ף', '{SIM} ז ס ב ה נ מ צ ת ץ . {SPACE}'], 'default': ['{MIC} й ц у к е н г ш щ з х ъ {BKSP}', '{LANG} ё ф ы в а п р о л д ж э', '{SIM} я ч с м и т ь б ю . {SPACE}'] }, clarify: { 'en': ['1 2 3 4 5 6 7 8 9 0 - {BKSP}', 'q w e r t y u i o p', 'a s d f g h j k l', 'z x c v b n m .', '{MIC} {LANG} {SPACE} {SEARCH}'], 'uk': ['1 2 3 4 5 6 7 8 9 0 - {BKSP}', 'й ц у к е н г ш щ з х ї', 'ф і в а п р о л д ж є', 'я ч с м и т ь б ю .', '{MIC} {LANG} {SPACE} {SEARCH}'], 'he': ['1 2 3 4 5 6 7 8 9 0 - {BKSP}', 'ק ר א ט ו ן ם פ', 'ש ד ג כ ע י ח ל ך ף', 'ז ס ב ה נ מ צ ת ץ .', '{MIC} {LANG} {SPACE} {SEARCH}'], 'default': ['1 2 3 4 5 6 7 8 9 0 - {BKSP}', 'й ц у к е н г ш щ з х ъ', 'ф ы в а п р о л д ж э', 'я ч с м и т ь б ю .', '{MIC} {LANG} {SPACE} {SEARCH}'] }, "default": { 'en': ['{SIM} 1 2 3 4 5 6 7 8 9 0 - + = {BKSP}', '{LANG} q w e r t y u i o p', 'a s d f g h j k l / {ENTER}', '{SHIFT} z x c v b n m , . : ' + http, '{SPACE}'], 'uk': ['{SIM} 1 2 3 4 5 6 7 8 9 0 - + = {BKSP}', '{LANG} й ц у к е н г ш щ з х ї', 'ф і в а п р о л д ж є {ENTER}', '{SHIFT} я ч с м и т ь б ю . : ' + http, '{SPACE}'], 'he': ['{SIM} ק ר א ט ו ן ם פ {BKSP}', '{LANG} ש ד ג כ ע י ח ל ך ף', 'ז ס ב ה נ מ צ ת ץ . /', '{SHIFT} ! @ # $ % ^ & * ( ) {SPACE}'], 'sim': ['{ABC} 1 2 3 4 5 6 7 8 9 0 - + = {BKSP}', '{LANG} ! @ # $ % ^ & * ( ) [ ]', '- _ = + \\ | [ ] { }', '; : \' " , . < > / ?', '{SPACE}'], 'default': ['{SIM} 1 2 3 4 5 6 7 8 9 0 - + = {BKSP}', '{LANG} й ц у к е н г ш щ з х ъ', 'ф ы в а п р о л д ж э {ENTER}', '{SHIFT} я ч с м и т ь б ю , . : ' + http, '{SPACE}'] }, nums: { 'default': ['0 1 2 3 4 {BKSP}', '5 6 7 8 9 {ENTER}'] } }; function add$3(name, layout) { layers[name] = layout; } function addLang(name, code, layout) { layers[name][code] = layout; } function get$2(name) { return layers[name]; } var Layers = { add: add$3, addLang: addLang, get: get$2 }; /** * Виртуальная клавиатура * @param {object} [params] - параметры клавиатуры * @param {string} [params.layout] - имя слоя клавиатуры из Layers (default, extended, full, ru, en и т.д.) или сам слой * @param {boolean} [params.textarea=false] - использовать textarea вместо input (для мобильных устройств) * @param {boolean} [params.nomic=false] - отключить микрофон * @param {string} [params.keyboard] - тип клавиатуры (lampa или simple) * @return {Keyboard} */ function Keyboard() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _keyClass = window.SimpleKeyboard["default"], _keyBord; var _lang_codes = { 'ru': 'ru-RU', 'en': 'en-US', 'be': 'be-BY', 'uk': 'uk-UA', 'zh': 'zh-CN', 'bg': 'bg-BG', 'pt': 'pt-PT', 'cs': 'cs-CZ' }; var last; var ime; var recognition; var simple = Storage.field('keyboard_type') !== 'lampa'; var input; var last_value; var height = window.innerHeight; var mobile = Platform.screen('mobile') && params.textarea; var is_electron = Platform.is('electron'); if (params.keyboard) { simple = params.keyboard !== 'lampa'; } if (Platform.screen('mobile')) simple = true; this.listener = Subscribe$2(); this.create = function () { var _this = this; if (simple) { input = $(''); if (mobile) { input = $(''); input.on('input', function () { input[0].style.height = 'auto'; input[0].style.height = input[0].scrollHeight + 'px'; }); } var time_blur = 0; var time_focus = 0; var stated, ended; if (Platform.is('orsay')) { ime = new IMEShell_Common(); if ($('.settings-input--free').length > 0) { ime.setUseNumberMode(true); } ime.inputboxID = 'orsay-keyboard'; ime.setUseIMEDim(false); ime.setMaxlength(256); ime.inputTitle = Lang.translate('search_input') + "..."; ime.onKeyPressFunc = function (key, str, id) { switch (key) { case 29443: // Enter Key case 88: //return case 45: //exit _this.listener.send('enter'); ime.onClose(); input.blur(); break; } }; ime.setOnTextChangeFunc = function (e) { input.val(e); var now_value = input.val(); if (last_value !== now_value) { last_value = now_value; stated = ended = false; _this.listener.send('change', { value: now_value }); } }; } input.on('keyup change input keypress', function (e) { var now_value = input.val(); if (last_value !== now_value) { last_value = now_value; stated = ended = false; _this.listener.send('change', { value: now_value }); } }); input.on('blur', function () { Keypad.enable(); time_blur = Date.now(); if (Platform.is('android') && Platform.screen('tv')) input.attr('disabled', 'true'); _this.listener.send('blur'); }); input.on('focus', function () { if (!Platform.is('apple_tv')) Keypad.disable(); time_focus = Date.now(); if (!(Platform.is('browser') || Platform.desktop())) _this.listener.send('focus'); }); input.on('keyup', function (e) { if (time_focus + 1000 > Date.now()) return; var keys = [13, 65376, 29443, 117, 65385, 461, 27]; var valu = input.val(); var cart = e.target.selectionStart; if (keys.indexOf(e.keyCode) >= 0 && !(Utils$1.isTouchDevice() && params.textarea && Platform.screen('mobile'))) { e.preventDefault(); console.log('Keyboard', 'blur key:', e.keyCode, 'value:', valu); input.blur(); } if ((e.keyCode == 13 || e.keyCode == 65376) && !(Utils$1.isTouchDevice() && params.textarea && Platform.screen('mobile'))) _this.listener.send('enter'); if (e.keyCode == 37 && cart == 0 && height == window.innerHeight) { if (stated) input.blur(), _this.listener.send('left'); stated = true; ended = false; } if (e.keyCode == 39 && cart >= valu.length && height == window.innerHeight) { if (ended) input.blur(), _this.listener.send('right'); ended = true; stated = false; } if (e.keyCode == 40) { if (height == window.innerHeight) input.blur(), _this.listener.send('down'); } if (e.keyCode == 38) { if (height == window.innerHeight) input.blur(), _this.listener.send('up'); } }); input.on('hover:focus', function () { if (Platform.is('orsay')) ime.onShow(); input.removeAttr('disabled'); input.focus(); }); input.on('hover:enter', function () { if (time_blur + 1000 < Date.now()) { if (Platform.is('orsay')) ime.onShow(); input.focus(); if (Platform.is('apple_tv')) window.location.assign('lampa://openkeyboard'); } }); var keyboard = $('.simple-keyboard'); if (!is_electron && !Platform.is('orsay') && !Platform.is('apple_tv') && (window.SpeechRecognition || window.webkitSpeechRecognition) && !params.nomic && Platform.screen('tv')) { var mic = $("
    \n \n \n \n \n \n
    "); mic.on('hover:enter', function () { if (Platform.is('android')) { Android.voiceStart(); window.voiceResult = _this.value.bind(_this); } else if (recognition) { try { if (recognition.record) recognition.stop();else recognition.start(); } catch (e) { recognition.stop(); } } }); keyboard.addClass('simple-keyboard--with-mic').append(mic); } keyboard.append(input); if (mobile) keyboard.addClass('simple-keyboard--with-textarea'); if (Platform.screen('mobile') || Platform.mouse()) { var buttons = $('
    ' + Lang.translate('ready') + '
    ' + Lang.translate('cancel') + '
    '); buttons.find('.simple-keyboard-buttons__enter').on('click', function () { input.blur(); _this.listener.send('enter'); }); buttons.find('.simple-keyboard-buttons__cancel').on('click', function () { _this.value(''); Controller.back(); }); $('.simple-keyboard').append(buttons); } } else { var layout = typeof params.layout == 'string' ? Layers.get(params.layout) : params.layout || Layers.get('default'); var press = Date.now(); if (is_electron) { Arrays.getKeys(layout).forEach(function (code) { layout[code] = layout[code].map(function (row) { return row.replace('{MIC}', '').replace(/\s{2,}/g, ' ').trim(); }); }); } _keyBord = new _keyClass({ display: { '{BKSP}': ' ', '{ENTER}': ' ', '{SHIFT}': ' ', '{SPACE}': ' ', '{LANG}': ' ', '{ABC}': 'Aa', '{SIM}': '#+', '{SEARCH}': Lang.translate('search'), '{MIC}': "\n \n \n \n " }, layout: layout, onChange: function onChange(value) { _this.listener.send('change', { value: value }); }, onKeyPress: function onKeyPress(button) { if (Date.now() - press < 100) return; press = Date.now(); if (button === "{SHIFT}" || button === "{SIM}" || button === "{ABC}") _this._handle(button);else if (button === '{MIC}') { if (is_electron) return; if (Platform.is('android')) { Android.voiceStart(); window.voiceResult = _this.value.bind(_this); } else if (recognition) { try { if (recognition.record) recognition.stop();else recognition.start(); } catch (e) { recognition.stop(); } } } else if (button === '{LANG}') { var codes = Lang.codes(); var items = []; var select_code = _keyBord.options.layoutName.split('-')[0]; items.push({ title: codes.ru, value: 'default', selected: select_code == 'default' }); Arrays.getKeys(codes).forEach(function (code) { if (layout[code]) { items.push({ title: codes[code], value: code, selected: select_code == code }); } }); setTimeout(function () { Select.show({ title: Lang.translate('title_choice_language'), items: items, onSelect: function onSelect(item) { Select.hide(); Storage.set('keyboard_default_lang', item.value); var shifted = _keyBord.options.layoutName.split('-')[1] == 'shift'; var new_layout = item.value + (shifted ? '-shift' : ''); _this.shifted(!shifted, new_layout, item.value); _keyBord.setOptions({ layoutName: new_layout }); last = false; _keyBord.options.lastLayerSelect = _keyBord.options.layoutName; Controller.toggle('keybord'); $('.simple-keyboard').attr('shifted', Boolean(shifted)); Controller.collectionFocus($('.simple-keyboard [data-skbtn="{LANG}"]')[0], $('.simple-keyboard')); }, onBack: function onBack() { Select.hide(); Controller.toggle('keybord'); } }); }, 300); } else if (button === '{SPACE}') { _this.value(_keyBord.getInput() + ' '); } else if (button === '{BKSP}') { _this.value(_keyBord.getInput().slice(0, -1)); } else if (button === '{ENTER}' || button === '{SEARCH}') { _this.listener.send('enter'); } } }); var lang = Storage.get('keyboard_default_lang', Storage.get('language', 'ru')); _keyBord.setOptions({ layoutName: lang == 'ru' ? 'default' : Arrays.getKeys(layout).indexOf(lang) >= 0 ? lang : layout.en ? 'en' : 'default' }); $('.simple-keyboard').addClass('hg-style-' + (params.layout || 'default')); } this.speechRecognition(); }; this.speechRecognition = function () { var _this2 = this; var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; console.log('Speech', 'status:', SpeechRecognition ? true : false); if (SpeechRecognition) { recognition = new SpeechRecognition(); recognition.continuous = false; recognition.lang = _lang_codes[Storage.get('language', 'ru')] || 'en-US'; console.log('Speech', 'lang:', recognition.lang); recognition.addEventListener("start", function () { console.log('Speech', 'start'); $('.simple-keyboard [data-skbtn="{mic}"]').css('color', 'red'); $('.simple-keyboard-mic').addClass('record'); recognition.record = true; Noty.show(Lang.translate('keyboard_listen')); }); recognition.addEventListener("end", function () { console.log('Speech', 'end'); $('.simple-keyboard [data-skbtn="{mic}"]').css('color', 'white'); $('.simple-keyboard-mic').removeClass('record'); recognition.record = false; }); recognition.addEventListener("result", function (event) { console.log('Speech', 'result:', event.resultIndex, event.results[event.resultIndex]); var current = event.resultIndex; var transcript = event.results[current][0].transcript; console.log('Speech', 'transcript:', transcript); if (transcript.toLowerCase().trim() === "stop recording") { recognition.stop(); } else { if (transcript.toLowerCase().trim() === "reset input") { _this2.value(''); } else { _this2.value(transcript); } } }); recognition.addEventListener("error", function (event) { console.log('Speech', 'error:', event); if (event.error == 'not-allowed') { Noty.show(Lang.translate('keyboard_nomic')); } recognition.stop(); }); } else { $('.simple-keyboard [data-skbtn="{mic}"]').css('opacity', '0.3'); } }; this.value = function (value) { if (simple) { input.val(value); if (mobile) { input[0].style.height = 'auto'; input[0].style.height = input[0].scrollHeight + 'px'; } } else _keyBord.setInput(value); last_value = value; this.listener.send('change', { value: value }); }; this._layout = function () { var _this3 = this; var keys = $('.simple-keyboard .hg-button').addClass('selector'); Controller.collectionSet($('.simple-keyboard')); Controller.collectionFocus(last || keys[0], $('.simple-keyboard')); $('.simple-keyboard .hg-button:not(.binded)').on('hover:enter', function (e) { Controller.collectionFocus($(this)[0]); _keyBord.handleButtonClicked($(this).attr('data-skbtn'), e); }).on('hover:focus', function (e) { last = e.target; _this3.listener.send('hover', { button: e.target }); }); keys.addClass('binded'); $('.simple-keyboard').addClass('hg-style-' + (params.layout || 'default')); }; this.shifted = function (shifted, layout, code) { if (!(shifted && _keyBord.options.layout[layout])) { var shift_layer = Arrays.clone(_keyBord.options.layout[code]); shift_layer = shift_layer.map(function (button) { return button.toUpperCase(); }); _keyBord.options.layout[layout] = shift_layer; } }; this._handle = function (button) { var current_layout = _keyBord.options.layoutName, layout = 'default', focus; var shifted = current_layout.split('-')[1] == 'shift'; var code = current_layout.split('-')[0]; $('.simple-keyboard').attr('shifted', Boolean(!shifted)); if (button == '{SHIFT}') { if (shifted) layout = code;else layout = code + '-shift'; this.shifted(shifted, layout, code); } else if (button == '{SIM}') { layout = 'sim'; focus = '{ABC}'; _keyBord.options.lastLayerSelect = current_layout; } else if (button == '{ABC}') { layout = _keyBord.options.lastLayerSelect || 'default'; focus = '{SIM}'; } _keyBord.setOptions({ layoutName: layout }); last = false; Controller.toggle('keybord'); Controller.collectionFocus($('.simple-keyboard [data-skbtn="' + (focus || button) + '"]')[0], $('.simple-keyboard')); }; this.toggle = function () { var _this4 = this; Controller.add('keybord', { toggle: function toggle() { if (simple) { Controller.collectionSet($('.simple-keyboard')); Controller.collectionFocus(mobile ? $('.simple-keyboard-input') : false, $('.simple-keyboard')); } else _this4._layout(); }, update: function update() {}, up: function up() { if (!Navigator.canmove('up')) { _this4.listener.send('up'); } else Navigator.move('up'); }, down: function down() { if (!Navigator.canmove('down')) { _this4.listener.send('down'); } else Navigator.move('down'); }, left: function left() { if (!Navigator.canmove('left')) { _this4.listener.send('left'); } else Navigator.move('left'); }, right: function right() { if (!Navigator.canmove('right')) { _this4.listener.send('right'); } else Navigator.move('right'); }, back: function back() { _this4.listener.send('back'); } }); Controller.toggle('keybord'); }; this.destroy = function () { try { if (simple) { input.remove(); } else _keyBord.destroy(); if (recognition) recognition.stop(); } catch (e) {} this.listener.destroy(); Keypad.enable(); }; } var html$8, keyboard, input, input_value = ''; /** * Заустить редактор * @param {{title:string, value:string, free:boolean, nosave:boolean}} params * @param {function} call */ function edit(params, call) { html$8 = Template.get('settings_input'); input = html$8.find('.settings-input__input'); if ((params.keyboard || Storage.field('keyboard_type')) !== 'lampa') input.hide(); $('body').addClass('keyboard-input--visible').append(html$8); html$8.addClass('settings-input--align-' + (params.align || 'center')); var pass = function pass(v) { return params.password ? v.replace(/./g, '*') : v; }; keyboard = new Keyboard(params); keyboard.listener.follow('change', function (event) { input_value = event.value.trim(); input.toggleClass('filled', Boolean(event.value)); input.html(pass(Utils$1.inputDisplay(event.value))); }); keyboard.listener.follow('enter', function (event) { back$1(); call(input_value); }); keyboard.listener.follow('focus', function (event) { html$8.toggleClass('settings-input--focus', true); }); keyboard.listener.follow('blur', function (event) { html$8.toggleClass('settings-input--focus', false); }); html$8.toggleClass('settings-input--free', params.free ? true : false); $('.settings-input__links', html$8).toggleClass('hide', params.nosave ? true : false); if (params.title) html$8.find('.settings-input__content').prepend('
    ' + params.title + '
    '); keyboard.listener.follow('down', function (event) { if (params.nosave) return; var members = Storage.get('setting_member', []); var links = []; links.push({ title: (members.indexOf(input_value) == -1 ? Lang.translate('settings_add') : Lang.translate('settings_remove')) + ' ' + Lang.translate('settings_this_value'), subtitle: input_value, add: true }); members.forEach(function (link) { links.push({ title: link, subtitle: Lang.translate('settings_user_links'), url: link, member: true }); }); links = links.concat([{ title: '127.0.0.1:8090', subtitle: Lang.translate('settings_for_local'), url: '127.0.0.1:8090' }]); Select.show({ title: Lang.translate('title_links'), items: links, onSelect: function onSelect(a) { if (a.add) { if (members.indexOf(a.subtitle) == -1) { Arrays.insert(members, 0, a.subtitle); Noty.show(Lang.translate('settings_added') + ' (' + a.subtitle + ')'); } else { Arrays.remove(members, a.subtitle); Noty.show(Lang.translate('settings_removed') + ' (' + a.subtitle + ')'); } Storage.set('setting_member', members); } else { keyboard.value(a.url); } keyboard.toggle(); }, onLong: function onLong(a, elem) { if (a.member) { Arrays.remove(members, a.url); Noty.show(Lang.translate('settings_removed') + ' (' + a.url + ')'); Storage.set('setting_member', members); $(elem).css({ opacity: 0.4 }); } }, onBack: function onBack() { keyboard.toggle(); } }); }); keyboard.listener.follow('back', function () { back$1(); call(input_value); }); keyboard.create(); keyboard.value(params.value); keyboard.toggle(); input_value = params.value; return keyboard; } /** * Назад */ function back$1() { destroy$1(); Controller.toggle('settings_component'); } /** * Уничтожить */ function destroy$1() { keyboard.destroy(); html$8.remove(); $('body').removeClass('keyboard-input--visible'); html$8 = null; keyboard = null; input = null; } var Input = { edit: edit }; var values = {}; var defaults = {}; var listener$1 = Subscribe$2(); /** * Запуск */ function init$u() { if (Platform.is('tizen')) { select('player', { 'inner': '#{settings_param_player_inner}', 'tizen': 'Tizen' }, 'tizen'); select('player_iptv', { 'inner': '#{settings_param_player_inner}', 'tizen': 'Tizen' }, 'tizen'); select('player_torrent', { 'inner': '#{settings_param_player_inner}', 'tizen': 'Tizen' }, 'tizen'); } if (Platform.is('orsay')) { select('player', { 'inner': '#{settings_param_player_inner}', 'orsay': 'Orsay' }, 'orsay'); select('player_iptv', { 'inner': '#{settings_param_player_inner}', 'orsay': 'Orsay' }, 'orsay'); select('player_torrent', { 'inner': '#{settings_param_player_inner}', 'orsay': 'Orsay' }, 'orsay'); } else if (Platform.is('webos')) { select('player', { 'inner': '#{settings_param_player_inner}', 'webos': 'WebOS' }, 'inner'); select('player_iptv', { 'inner': '#{settings_param_player_inner}', 'webos': 'WebOS' }, 'inner'); select('player_torrent', { 'inner': '#{settings_param_player_inner}', 'webos': 'WebOS' }, 'inner'); } else if (Platform.is('android')) { select('player', { 'inner': '#{settings_param_player_inner}', 'android': 'Android' }, 'android'); select('player_iptv', { 'inner': '#{settings_param_player_inner}', 'android': 'Android' }, 'android'); select('player_torrent', { 'android': 'Android' }, 'android'); trigger('internal_torrclient', false); } else if (Platform.desktop() && !Platform.macOS()) { select('player', { 'inner': '#{settings_param_player_inner}', 'other': '#{settings_param_player_outside}' }, 'inner'); select('player_iptv', { 'inner': '#{settings_param_player_inner}', 'other': '#{settings_param_player_outside}' }, 'inner'); select('player_torrent', { 'inner': '#{settings_param_player_inner}', 'other': '#{settings_param_player_outside}' }, 'inner'); } else if (Platform.macOS()) { select('player', { 'inner': '#{settings_param_player_inner}', 'iina': 'IINA', 'infuse': 'Infuse', 'mpv': 'MPV', 'nplayer': 'nPlayer', 'senplayer': 'SenPlayer', 'tracyplayer': 'TracyPlayer' }, 'inner'); select('player_iptv', { 'inner': '#{settings_param_player_inner}', 'iina': 'IINA', 'infuse': 'Infuse', 'mpv': 'MPV', 'nplayer': 'nPlayer', 'senplayer': 'SenPlayer', 'tracyplayer': 'TracyPlayer' }, 'inner'); select('player_torrent', { 'inner': '#{settings_param_player_inner}', 'iina': 'IINA', 'infuse': 'Infuse', 'mpv': 'MPV', 'nplayer': 'nPlayer', 'senplayer': 'SenPlayer', 'tracyplayer': 'TracyPlayer' }, 'inner'); } else if (Platform.is('apple')) { select('player', { 'inner': '#{settings_param_player_inner}', 'ios': 'iOS', 'vlc': 'VLC', 'nplayer': 'nPlayer', 'infuse': 'Infuse', 'vidhub': 'Vidhub', 'svplayer': 'SVPlayer', 'tracyplayer': 'TracyPlayer', 'senplayer': 'SenPlayer' }, 'inner'); select('player_iptv', { 'inner': '#{settings_param_player_inner}', 'ios': 'iOS', 'vlc': 'VLC', 'nplayer': 'nPlayer', 'infuse': 'Infuse', 'svplayer': 'SVPlayer', 'tracyplayer': 'TracyPlayer', 'senplayer': 'SenPlayer' }, 'inner'); select('player_torrent', { 'inner': '#{settings_param_player_inner}', 'ios': 'iOS', 'vlc': 'VLC', 'nplayer': 'nPlayer', 'infuse': 'Infuse', 'vidhub': 'Vidhub', 'svplayer': 'SVPlayer', 'tracyplayer': 'TracyPlayer', 'senplayer': 'SenPlayer' }, 'inner'); } else if (Platform.is('apple_tv')) { select('player', { 'tvospro': '#{settings_param_player_inner_tvos} tvOS Pro', 'tvos': '#{settings_param_player_inner_tvos} tvOS Universal', 'tvosl': '#{settings_param_player_inner_tvos} tvOS Online', 'tvosSelect': '#{settings_param_player_outside}', 'vlc': 'VLC', 'infuse': 'Infuse', 'senplayer': 'SenPlayer', 'vidhub': 'Vidhub', 'inner': '#{settings_param_player_inner}', 'svplayer': 'SVPlayer' }, 'tvos'); select('player_iptv', { 'tvospro': '#{settings_param_player_inner_tvos} tvOS Pro', 'tvos': '#{settings_param_player_inner_tvos} tvOS Universal', 'tvosl': '#{settings_param_player_inner_tvos} tvOS Online', 'tvosSelect': '#{settings_param_player_outside}', 'vlc': 'VLC', 'infuse': 'Infuse', 'senplayer': 'SenPlayer', 'vidhub': 'Vidhub', 'inner': '#{settings_param_player_inner}', 'svplayer': 'SVPlayer' }, 'inner'); select('player_torrent', { 'tvospro': '#{settings_param_player_inner_tvos} tvOS Pro', 'tvos': '#{settings_param_player_inner_tvos} tvOS Universal', 'tvosSelect': '#{settings_param_player_outside}', 'infuse': 'Infuse', 'senplayer': 'SenPlayer', 'vidhub': 'Vidhub', 'vlc': 'VLC', 'inner': '#{settings_param_player_inner}', 'svplayer': 'SVPlayer' }, 'tvos'); } trigger('glass_style', Platform.screen('mobile')); trigger('advanced_animation', Platform.is('apple_tv') || Platform.is('browser') || Platform.desktop() || navigator.userAgent.toLowerCase().indexOf('shield') >= 0); var screensaver_types = { 'nature': '#{settings_param_screensaver_nature}', 'chrome': 'ChromeCast', 'cub': 'CUB', 'aerial': 'Aerial' }; select('screensaver_type', screensaver_types, 'aerial'); select('keyboard_type', { 'lampa': '#{settings_param_keyboard_lampa}', 'integrate': '#{settings_param_keyboard_system}' }, Platform.screen('mobile') || Platform.is('apple_tv') || Platform.macOS() || Platform.desktop() || Platform.is('browser') ? 'integrate' : 'lampa'); select('navigation_type', { 'controll': '#{settings_param_navigation_remote}', 'mouse': '#{settings_param_navigation_mouse}', 'touch': '#{settings_param_navigation_touch}' }, Platform.is('browser') || Platform.desktop() ? 'mouse' : Platform.screen('mobile') ? 'touch' : 'controll'); //язык и комбинации для поиска var langcode = Storage.get('language', 'ru'); var langname = Lang.codes()[langcode]; var selector = { 'df': '#{settings_param_torrent_lang_orig}', 'df_year': '#{settings_param_torrent_lang_orig} + #{torrent_parser_year}', 'df_lg': '#{settings_param_torrent_lang_orig} + ' + langname, 'df_lg_year': '#{settings_param_torrent_lang_orig} + ' + langname + ' + #{torrent_parser_year}', 'lg': langname, 'lg_year': langname + ' + #{torrent_parser_year}', 'lg_df': langname + ' + #{settings_param_torrent_lang_orig}', 'lg_df_year': langname + ' + #{settings_param_torrent_lang_orig} + #{torrent_parser_year}' }; if (Arrays.getKeys(selector).indexOf(Storage.get('parse_lang', 'df')) == -1) Storage.set('parse_lang', 'df'); select('parse_lang', selector, 'df'); select('tmdb_lang', Lang.codes(), 'ru'); // баг со старыми телеками, неправильно работает Utils.protocol() // let agent = navigator.userAgent.toLowerCase() // let versi = agent.match(/chrome\/(\d+)/) // versi = versi ? parseInt(versi[1]) : 60 // versi = isNaN(versi) ? 60 : versi select('protocol', { 'http': '#{settings_param_no}', 'https': '#{settings_param_yes}' }, 'https'); trigger('request_caching', Platform.is('orsay') || Platform.is('netcast') ? false : true); } /** * Переключатель * @param {string} name - название * @param {boolean} value_default - значение по дефолту */ function trigger(name, value_default) { values[name] = { 'true': '#{settings_param_yes}', 'false': '#{settings_param_no}' }; defaults[name] = value_default; } /** * Выбрать * @param {string} name - название * @param {{key:string}} select_data - значение * @param {string} select_default_value - значение по дефолту */ function select(name, select_data, select_default_value) { values[name] = select_data; defaults[name] = select_default_value; } /** * Биндит события на элемент * @param {object} elems */ function bind(elems, elems_html) { elems.on('hover:enter', function (event) { var elem = $(event.target); var type = elem.data('type'); var name = elem.data('name'); var onChange = elem.data('onChange'); if (type == 'toggle') { var params = values[name]; var keys = Arrays.isArray(params) ? params : Arrays.getKeys(params), value = Storage.get(name, defaults[name]) + '', position = keys.indexOf(value); position++; if (position >= keys.length) position = 0; position = Math.max(0, Math.min(keys.length - 1, position)); value = keys[position]; Storage.set(name, value); update$3(elem, elems, elems_html); if (onChange) onChange(value); } if (type == 'input') { Input.edit({ elem: elem, name: name, nomic: true, value: elem.data('string') ? window.localStorage.getItem(name) || defaults[name] : Storage.get(name, defaults[name]) + '' }, function (new_value) { Storage.set(name, new_value); update$3(elem, elems, elems_html); if (onChange) onChange(new_value); }); } if (type == 'button') { listener$1.send('button', { name: name }); if (onChange) onChange(); } if (type == 'add') { Input.edit({ value: '' }, function (new_value) { if (new_value && Storage.add(name, new_value)) { displayAddItem(elem, new_value); listener$1.send('update_scroll'); } }); } if (type == 'select') { var _params = values[name]; var _value = Storage.get(name, defaults[name]) + ''; var items = []; for (var i in _params) { items.push({ title: Lang.translate(_params[i]), value: i, selected: i == _value }); } var enabled = Controller.enabled().name; Select.show({ title: Lang.translate('title_choice'), items: items, onBack: function onBack() { Controller.toggle(enabled); }, onSelect: function onSelect(a) { Storage.set(name, a.value); update$3(elem, elems, elems_html); Controller.toggle(enabled); if (onChange) onChange(a.value); } }); } }).on('hover:hover hover:touch', function (e) { Navigator.focused(e.target); }).each(function () { if (!$(this).data('static')) update$3($(this), elems, elems_html); }); if (elems.eq(0).data('type') == 'add') { displayAddList(elems.eq(0)); } } /** * Добавить дополнительное полу * @param {object} elem * @param {object} element */ function displayAddItem(elem, element) { var name = elem.data('name'); var item = $('
    ' + element + '
    ' + '
    '); item.on('hover:long', function () { var list = Storage.get(name, '[]'); Arrays.remove(list, element); Storage.set(name, list); item.css({ opacity: 0.5 }); }); elem.after(item); } /** * Вывести дополнительные поля * @param {object} elem */ function displayAddList(elem) { var list = Storage.get(elem.data('name'), '[]'); list.forEach(function (element) { displayAddItem(elem, element); }); listener$1.send('update_scroll'); } /** * Обновляет значения на элементе * @param {object} elem */ function update$3(elem, elems, elems_html) { var name = elem.data('name'); var key = elem.data('string') ? window.localStorage.getItem(name) || defaults[name] : Storage.get(name, defaults[name] + ''); var val = typeof values[name] == 'string' ? key : values[name][key] || values[name][defaults[name]]; var plr = elem.attr('placeholder'); if (!val && plr) val = plr; elem.find('.settings-param__value').text(Lang.translate(val)); var children = elem.data('children'); if (children) { var parent = elems_html ? elems_html.find('[data-parent="' + children + '"]') : elems.filter('[data-parent="' + children + '"]'); var value = elem.data('children-value'); var visibl = value ? Storage.field(name) !== value : !Storage.field(name); if (elem.data('children-reverse')) visibl = !visibl; parent.toggleClass('hide', visibl); parent.filter('[data-visible-value]').each(function () { $(this).toggleClass('hide', $(this).data('visible-value') !== key); }); parent.filter('[data-visible-value-in]').each(function () { var valueStr = this.getAttribute('data-visible-value-in'); var valueList = valueStr.split(',').map(function (item) { return item.trim().toLowerCase(); }); var isVisible = false; for (var i = 0; i < valueList.length; i++) { if (key.toLowerCase().indexOf(valueList[i]) !== -1) { isVisible = true; break; } } $(this).toggleClass('hide', !isVisible); }); listener$1.send('update_scroll_position'); } updateInfuseLaunchVisibility(elems_html); } function isInfusePlayerSelected() { return ['player', 'player_iptv', 'player_torrent'].some(function (name) { return Storage.field(name) === 'infuse'; }); } function updateInfuseLaunchVisibility(elems_html) { if (!elems_html || !elems_html.length) return; elems_html.find('[data-infuse-launch]').toggleClass('hide', !isInfusePlayerSelected()); } /** * Получить значение параметра * @param {string} name * @returns * */ function field$1(name) { return Storage.get(name, defaults[name] + ''); } /** * Добовляем селекторы */ select('interface_size', { 'small': '#{settings_param_interface_size_small}', 'normal': '#{settings_param_interface_size_normal}', 'bigger': '#{settings_param_interface_size_bigger}' }, 'normal'); select('navigation_type', { 'controll': '#{settings_param_navigation_remote}', 'mouse': '#{settings_param_navigation_mouse}', 'touch': '#{settings_param_navigation_touch}' }, 'controll'); select('poster_size', { 'w200': '#{settings_param_poster_quality_low}', 'w300': '#{settings_param_poster_quality_average}', 'w500': '#{settings_param_poster_quality_high}' }, 'w300'); select('parser_torrent_type', { 'jackett': 'Jackett', 'prowlarr': 'Prowlarr', 'torrserver': 'TorrServer' }, 'jackett'); select('parser_use_link', { 'one': '#{settings_param_link_use_one}', 'two': '#{settings_param_link_use_two}', 'both': '#{settings_param_link_use_both}' }, 'one'); select('jackett_interview', { 'all': '#{settings_param_jackett_interview_all}', 'healthy': '#{settings_param_jackett_interview_healthy}' }, 'all'); select('background_type', { 'complex': '#{settings_param_background_complex}', 'simple': '#{settings_param_background_simple}', 'poster': '#{settings_param_background_image}' }, 'simple'); select('pages_save_total', { '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '10': '10', '20': '20' }, '5'); select('player', { 'inner': '#{settings_param_player_inner}' }, 'inner'); select('player_iptv', { 'inner': '#{settings_param_player_inner}' }, 'inner'); select('player_torrent', { 'inner': '#{settings_param_player_inner}' }, 'inner'); select('torrserver_use_link', { 'one': '#{settings_param_link_use_one}', 'two': '#{settings_param_link_use_two}' }, 'one'); select('torrserver_search_type', { 'rutor': '#{settings_param_torrserver_search_rutor}', 'torznab': '#{settings_param_torrserver_search_torznab}', 'both': '#{settings_param_torrserver_search_both}' }, 'both'); select('subtitles_size', { 'small': '#{settings_param_subtitles_size_small}', 'normal': '#{settings_param_subtitles_size_normal}', 'large': '#{settings_param_subtitles_size_bigger}' }, 'normal'); select('subtitles_lang', filter_langs.reduce(function (result, lang) { result[lang.code] = lang.title; return result; }, { 'auto': '#{settings_param_subtitles_lang_auto}' }), 'auto'); select('screensaver_time', { '1': '1', '2': '2', '5': '5', '10': '10' }, '5'); select('parse_lang', { 'df': '#{settings_param_torrent_lang_orig}' }, 'df'); select('parse_timeout', { '15': '15', '30': '30', '60': '60', '90': '90', '120': '120' }, '15'); select('player_rewind', { '5': '5', '10': '10', '15': '15', '20': '20', '30': '30', '50': '50', '100': '100' }, '20'); select('player_timecode', { 'again': '#{settings_param_player_timecode_again}', 'continue': '#{settings_param_player_timecode_continue}', 'ask': '#{settings_param_player_timecode_ask}' }, 'continue'); select('infuse_launch_mode', { 'ask': '#{settings_infuse_launch_ask}', 'play': '#{settings_infuse_launch_play}', 'save_and_play': '#{settings_infuse_launch_save_and_play}' }, 'play'); select('player_scale_method', { 'transform': 'Transform', 'calculate': '#{settings_param_player_scale_method}' }, 'transform'); select('player_hls_method', { 'application': '#{settings_param_player_hls_app}', 'hlsjs': '#{settings_param_player_hls_js}' }, 'application'); select('source', { 'tmdb': 'TMDB', 'cub': 'CUB' }, 'tmdb'); select('start_page', { 'main': '#{title_main}', 'favorite@bookmarks': '#{settings_input_links}', 'favorite@history': '#{title_history}', 'mytorrents': '#{title_mytorrents}', 'last': '#{title_last}' }, 'main'); select('scroll_type', { 'css': 'CSS', 'js': 'Javascript' }, 'css'); select('card_views_type', { 'preload': '#{settings_param_card_view_load}', 'view': '#{settings_param_card_view_all}' }, 'preload'); select('keyboard_type', { 'lampa': '#{settings_param_keyboard_lampa}', 'integrate': '#{settings_param_keyboard_system}' }, 'lampa'); select('card_interfice_type', { 'old': '#{settings_param_card_interface_old}', 'new': '#{settings_param_card_interface_new}' }, 'new'); select('glass_opacity', { 'easy': '#{settings_param_glass_easy}', 'medium': '#{settings_param_glass_medium}', 'blacked': '#{settings_param_glass_blacked}' }, 'easy'); select('interface_sound_level', { '100': '100', '80': '80', '60': '60', '40': '40', '20': '20' }, '60'); select('time_offset', { 'n-10': '-10', 'n-9': '-9', 'n-8': '-8', 'n-7': '-7', 'n-6': '-6', 'n-5': '-5', 'n-4': '-4', 'n-3': '-3', 'n-2': '-2', 'n-1': '-1', 'n0': '0', 'n1': '1', 'n2': '2', 'n3': '3', 'n4': '4', 'n5': '5', 'n6': '6', 'n7': '7', 'n8': '8', 'n9': '9', 'n10': '10' }, 'n0'); select('video_quality_default', { '480': '480p', '720': '720p', '1080': '1080p', '1440': '1440p', '2160': '2160p' }, '1080'); select('player_launch_trailers', { 'inner': '#{settings_param_player_inner}', 'youtube': 'YouTube' }, 'inner'); var mirrors_select = {}; object$2.cub_mirrors.forEach(function (mirror) { mirrors_select[mirror] = mirror; }); select('cub_domain', mirrors_select, object$2.cub_domain); /** * Добовляем триггеры */ trigger('animation', true); trigger('background', true); trigger('torrserver_savedb', false); trigger('torrserver_preload', true); trigger('parser_use', false); trigger('cloud_use', false); trigger('account_use', false); trigger('torrserver_auth', false); trigger('torrserver_gts', false); trigger('torrserver_tracktimecode', false); trigger('mask', true); trigger('playlist_next', true); trigger('internal_torrclient', true); trigger('subtitles_stroke', true); trigger('subtitles_backdrop', false); trigger('screensaver', true); trigger('proxy_tmdb', true); trigger('proxy_tmdb_auto', true); trigger('proxy_other', true); trigger('parse_in_search', false); trigger('subtitles_start', false); trigger('helper', true); trigger('light_version', false); trigger('player_normalization', false); trigger('card_quality', true); trigger('card_episodes', true); trigger('card_interfice_poster', true); trigger('glass_style', false); trigger('black_style', false); trigger('hide_outside_the_screen', true); trigger('card_interfice_cover', true); trigger('card_interfice_reactions', true); trigger('cache_images', false); trigger('interface_sound_play', false); trigger('menu_always', false); trigger('player_external_fullscreen', true); trigger('adult_content_view', false); /** * Добовляем поля */ select('jackett_url', '', ''); select('jackett_key', '', ''); select('jackett_url_two', '', ''); select('jackett_key_two', '', ''); select('prowlarr_url', '', ''); select('prowlarr_key', '', ''); select('prowlarr_url_two', '', ''); select('prowlarr_key_two', '', ''); select('torrserver_url', '', ''); select('torrserver_url_two', '', ''); select('torrserver_login', '', ''); select('torrserver_password', '', ''); select('parser_website_url', '', ''); select('cloud_token', '', ''); select('account_email', '', ''); select('account_password', '', ''); select('device_name', '', 'Lampa'); select('player_nw_path', '', 'C:/Program Files/VideoLAN/VLC/vlc.exe'); select('tmdb_proxy_api', '', ''); select('tmdb_proxy_image', '', ''); var Params = { listener: listener$1, init: init$u, bind: bind, update: update$3, field: field$1, select: select, trigger: trigger, values: values, defaults: defaults }; var network$1 = new Request(); function api$1() { return Utils$1.protocol() + object$2.cub_domain + '/api/'; } var WorkerArray = /*#__PURE__*/function () { function WorkerArray(field) { _classCallCheck(this, WorkerArray); this.field = field; this.empty = []; this.data = []; this.limit = 3000; this.loaded = false; this.update_time = 0; this.send_timers = {}; this.send_rate = { count: 0, reset: 0 }; } _createClass(WorkerArray, [{ key: "init", value: function init(class_type) { var _this = this; var timer_update; var start_time = Date.now(); this.class_type = class_type; console.log('StorageWorker', this.field, 'start follow'); Storage.listener.follow('change', function (e) { if (_this.field == e.name && _this.loaded && Account$1.Permit.sync && Account$1.hasPremium()) { try { _this.save(e.value); } catch (e) { console.log('StorageWorker', _this.field, e.message); } } if (e.name == 'account') { clearTimeout(timer_update); timer_update = setTimeout(_this.update.bind(_this, true), 5 * 1000); } }); Socket.listener.follow('message', function (e) { if (e.method == 'storage' && e.data.name == _this.field) { try { if (e.data.remove) _this.removeFromSocket(e.data);else _this.updateFromSocket(e.data); } catch (e) { console.log('StorageWorker', _this.field, e.message); } } }); Socket.listener.follow('open', function (e) { if (Date.now() - start_time > 1000 * 60 * 5) { clearTimeout(timer_update); timer_update = setTimeout(_this.update.bind(_this, false, true), 10 * 1000); } }); this.update(); } }, { key: "restrict", value: function restrict(result) { if (Arrays.isObject(result)) { var keys = Arrays.getKeys(result); if (keys.length > this.limit) { var remv = keys.slice(0, keys.length - this.limit); remv.forEach(function (k) { delete result[k]; }); } } else if (result.length > this.limit) { result = result.slice(result.length - this.limit); } return result; } }, { key: "parse", value: function parse(from, nolisten) { var to = Storage.cache(this.field, this.limit, Arrays.clone(this.empty)); this.filter(from, to); Storage.set(this.field, to, nolisten); this.data = this.restrict(Arrays.decodeJson(localStorage.getItem(this.field), Arrays.clone(this.empty))); Lampa.Listener.send('worker_storage', { type: 'insert', name: this.field, from: from, to: to }); } }, { key: "filter", value: function filter(from, to) { from.forEach(function (a) { if (to.indexOf(a) == -1) to.push(a); }); } }, { key: "update", value: function update(full, nolisten) { var _this2 = this; if (Account$1.Permit.sync && Account$1.hasPremium() && Date.now() - this.update_time > 1000 * 60 * 9) { var account = Account$1.Permit.account; this.update_time = Date.now(); var url = api$1() + 'storage/data/' + encodeURIComponent(this.field) + '/' + this.class_type; var all = full; if (Storage.get('storage_' + this.field + '_update_time', '0') + 1000 * 60 * 60 * 24 < Date.now()) all = true; if (all) url = url + '?full=true'; network$1.silent(url, function (result) { try { _this2.parse(result.data, nolisten); } catch (e) { console.log('StorageWorker', _this2.field, e.message); } Storage.set('storage_' + _this2.field + '_update_time', Date.now()); _this2.loaded = true; }, function (e) { console.log('StorageWorker', _this2.field, e.decode_error); }, false, { headers: { token: account.token, profile: account.profile.id } }); } } }, { key: "removeFromSocket", value: function removeFromSocket(data) { Arrays.remove(this.data, data.value); var store = Storage.cache(this.field, this.limit, Arrays.clone(this.empty)); Arrays.remove(store, data.value); Storage.set(this.field, store, true); } }, { key: "updateFromSocket", value: function updateFromSocket(data) { var from = [data.value]; this.parse(from, true); } }, { key: "send", value: function send(id, value) { var _this3 = this; if (!Account$1.hasPremium()) return; clearTimeout(this.send_timers[id]); this.send_timers[id] = setTimeout(function () { delete _this3.send_timers[id]; var now = Date.now(); if (now > _this3.send_rate.reset) { _this3.send_rate.count = 0; _this3.send_rate.reset = now + 2000; } if (_this3.send_rate.count >= 5) { console.log('StorageWorker', 'rate limit:', _this3.field, id); return; } _this3.send_rate.count++; console.log('StorageWorker', 'save:', _this3.field, id, value); var str = JSON.stringify(value); if (str.length < 10000) { Socket.send('storage', { params: { id: id, name: _this3.field, value: value } }); } }, 2000); } }, { key: "sendRemove", value: function sendRemove(id, value) { var str = JSON.stringify(value); console.log('StorageWorker', 'remove:', this.field, id, value); if (str.length < 10000) { Socket.send('storage', { params: { id: id, name: this.field, value: value, remove: true } }); } } }, { key: "sendClean", value: function sendClean() { Socket.send('storage', { params: { id: null, name: this.field, value: '', clean: true } }); } }, { key: "save", value: function save(value) { var _this4 = this; var uniq = value.filter(function (a) { return _this4.data.indexOf(a) == -1; }); uniq.forEach(function (val) { _this4.data.push(val); _this4.send(null, val); }); } }, { key: "remove", value: function remove(value) { Arrays.remove(this.data, value); this.sendRemove(null, value); } }, { key: "clean", value: function clean() { this.data = []; this.sendClean(); } }]); return WorkerArray; }(); var WorkerFilterID = /*#__PURE__*/function (_WorkerArray) { _inherits(WorkerFilterID, _WorkerArray); var _super = _createSuper(WorkerFilterID); function WorkerFilterID() { _classCallCheck(this, WorkerFilterID); return _super.apply(this, arguments); } _createClass(WorkerFilterID, [{ key: "filter", value: function filter(from, to) { from.forEach(function (a) { var find = to.find(function (b) { return b.id == a.id; }); if (!find) to.push(a);else { to[to.indexOf(find)] = a; } }); } }, { key: "removeFromSocket", value: function removeFromSocket(data) { var find = this.data.find(function (a) { return a.id == data.id; }); if (find) Arrays.remove(this.data, find); var store = Storage.cache(this.field, this.limit, Arrays.clone(this.empty)); find = store.find(function (a) { return a.id == data.id; }); if (find) Arrays.remove(store, find); Storage.set(this.field, store, true); } }, { key: "updateFromSocket", value: function updateFromSocket(data) { var from = [data.value]; this.parse(from, true); } }, { key: "save", value: function save(value) { var _this5 = this; var uniq = []; value.forEach(function (val) { var find = _this5.data.find(function (a) { return a.id == val.id; }); if (!find) { _this5.data.push(_typeof(val) == 'object' ? Arrays.clone(val) : val); uniq.push(val); } else if (JSON.stringify(val) !== JSON.stringify(find)) { _this5.data[_this5.data.indexOf(find)] = _typeof(val) == 'object' ? Arrays.clone(val) : val; uniq.push(val); } }); uniq.forEach(function (val) { _this5.send(val.id, val); }); } }, { key: "remove", value: function remove(id) { var find = this.data.find(function (a) { return a.id == id; }); if (find) Arrays.remove(this.data, find); this.sendRemove(id, null); } }]); return WorkerFilterID; }(WorkerArray); var WorkerObject = /*#__PURE__*/function (_WorkerArray2) { _inherits(WorkerObject, _WorkerArray2); var _super2 = _createSuper(WorkerObject); function WorkerObject(params) { var _this6; _classCallCheck(this, WorkerObject); _this6 = _super2.call(this, params); _this6.data = {}; _this6.empty = {}; return _this6; } _createClass(WorkerObject, [{ key: "filter", value: function filter(from, to) { for (var _id in from) { to[_id] = from[_id]; } } }, { key: "removeFromSocket", value: function removeFromSocket(data) { delete this.data[id]; var store = Storage.cache(this.field, this.limit, Arrays.clone(this.empty)); delete store[id]; Storage.set(this.field, store, true); } }, { key: "updateFromSocket", value: function updateFromSocket(data) { var object = {}; object[data.id] = data.value; this.parse(object, true); } }, { key: "save", value: function save(value) { var _this7 = this; var uniq = []; for (var _id2 in value) { var a = value[_id2]; var b = this.data[_id2]; if (!this.data[_id2]) { this.data[_id2] = _typeof(a) == 'object' ? Arrays.clone(a) : a; uniq.push(_id2); } else { a = JSON.stringify(a); b = JSON.stringify(b); if (a !== b) { this.data[_id2] = _typeof(value[_id2]) == 'object' ? Arrays.clone(value[_id2]) : value[_id2]; uniq.push(_id2); } } } uniq.forEach(function (id) { _this7.send(id, value[id]); }); } }, { key: "remove", value: function remove(id) { delete this.data[id]; this.sendRemove(id, null); } }, { key: "clean", value: function clean() { this.data = {}; this.sendClean(); } }]); return WorkerObject; }(WorkerArray); var Workers = { //['string',0499383] array_string: WorkerArray, //[{'id':'049994',...}] array_object_id: WorkerFilterID, //{'id048994':{...}, ...} object_object: WorkerObject, //{'id399884':'string', ...} object_string: WorkerObject }; var listener = Subscribe$2(); var readed = {}; var workers = {}; var reserve = {}; function init$t() { sync('online_view', 'array_string'); sync('torrents_view', 'array_string'); sync('search_history', 'array_string'); sync('online_last_balanser', 'object_string'); sync('user_clarifys', 'object_object'); sync('torrents_filter_data', 'object_object'); Socket.listener.follow('send', function (e) { if (e.method == 'storage') { Api$1.load('storage/update', {}, { json: JSON.stringify(e.data.params) })["catch"](function (e) {}); } }); } /** * Загрузка кеша из IndexedDB * @doc * @name task * @alias Storage * @param {function} next функция обратного вызова */ function task(next) { Cache.getData('storage').then(function (result) { if (result) { console.log('Storage', 'load cache:', result.length); result.forEach(function (data) { reserve[data.key] = data.value; }); } next(); })["catch"](function (e) { console.log('Storage', 'cache error:', e.message, e.stack, e); next(); }); } /** * Получить значение переменной из оператвной памяти localStorage * @doc * @name get * @alias Storage * @param {string} name название * @param {string} empty значение по умолчанию * @returns {any} */ function get$1(name, empty) { var item; var value = readed[name]; if (typeof value == 'undefined') { item = window.localStorage.getItem(name); value = item; if (item == null && reserve[name]) { value = reserve[name]; } } value = value || empty || ''; if (value == 'true' || value == 'false') return value == 'true' ? true : false; if (readed[name] && (Arrays.isObject(value) || Arrays.isArray(value))) return readed[name]; var convert = parseInt(value); if (!isNaN(convert) && /^\d+$/.test(value)) return convert; var i = typeof value == 'string' ? value[0] : ''; if (i == '[' || i == '{') { try { value = JSON.parse(value); } catch (error) {} } readed[name] = value; return value; } /** * Получить значение переменной напрямую из localStorage * @doc * @name value * @alias Storage * @param {string} name название * @param {string} empty значение по умолчанию * @returns {string} */ function value(name, empty) { return window.localStorage.getItem(name) || empty || ''; } /** * Установить значение переменной в оператвную память и localStorage * @doc * @name set * @alias Storage * @param {string} name название * @param {any} value значение * @param {boolean} nolisten по умолчанию `false`, если `true`, то не отправлять событие об изменении * @returns {string} */ function set(name, value, nolisten, callerror) { readed[name] = value; var write = ''; try { write = Arrays.isObject(value) || Arrays.isArray(value) ? JSON.stringify(value) : value; } catch (e) { console.log('Storage', name, 'JSON.stringify error:', e, value); } try { window.localStorage.setItem(name, write); } catch (e) { if (e.name == 'QuotaExceededError') { console.log('Storage', 'QuotaExceededError:', name, value); window.localStorage.removeItem(name); Cache.rewriteData('storage', name, { key: name, value: write }).then(function () { reserve[name] = write; })["catch"](function (e) { console.log('Storage', 'Cache error:', e.message, e.stack, e); }); if (callerror) callerror(e); } } if (!nolisten) listener.send('change', { name: name, value: value }); } /** * Добавить значение в массив к уже существующим * @doc * @name add * @alias Storage * @param {string} name название * @param {any} new_value значение * @param {boolean} nolisten по умолчанию `false`, если `true`, то не отправлять событие об изменении * @returns {boolean} true если значение добавлено, false если значение уже существует */ function add$2(name, new_value, nolisten) { var list = get$1(name, '[]'); if (list.indexOf(new_value) == -1) { list.push(new_value); set(name, list, nolisten); if (!nolisten) listener.send('add', { name: name, value: new_value }); return true; } } /** * Значение по умолчанию из параметров лампы * @doc * @name field * @alias Storage * @param {string} name название * @returns {any} значение */ function field(name) { return Params.field(name); } /** * Записать значение в кэш с ограничением по количеству данных * @doc * @name cache * @alias Storage * @param {string} name название * @param {integer} max максимальное количество данных * @param {string} empty значение по умолчанию * @returns {any} значение */ function cache(name, max, empty) { var result = get$1(name, JSON.stringify(empty)); if (Arrays.isObject(empty)) { var keys = Arrays.getKeys(result); if (keys.length > max) { var remv = keys.slice(0, keys.length - max); remv.forEach(function (k) { delete result[k]; }); set(name, result); } } else if (result.length > max) { result = result.slice(result.length - max); set(name, result); } return result; } /** * Добавить переменную в синхронизацию с сервером * @doc * @name sync * @alias Storage * @param {string} field_name название * @param {string} class_type тип данных, доступно (array_string | array_object_id | object_string | object_object) \n\n `array_string` - ['a','b','c'] \n\n `array_object_id` - [{id:1,..},{id:2,..}] \n\n `object_string` - {a:'a',b:'b'} \n\n `object_object` - {a:{a:'a'},b:{b:'b'}} */ function sync(field_name, class_type) { if (Workers[class_type] && !workers[field_name]) { workers[field_name] = new Workers[class_type](field_name); workers[field_name].init(class_type); } } /** * Удалить значение из синхронизации * @doc * @name remove * @alias Storage * @param {string} field_name название * @param {any} value значение */ function remove(field_name, value) { if (workers[field_name]) workers[field_name].remove(value); } /** * Очистить полностью значение из синхронизации * @doc * @name clean * @alias Storage * @param {string} field_name название */ function clean(field_name) { if (workers[field_name]) workers[field_name].clean(); } /** * Очистить кэш * @doc * @name clear * @alias Storage * @param {function} full `true` - полностью очистить, `false` - очистить только кеш */ function clear$1(full) { listener.send('clear', { full: full }); if (full) { Noty.show(Lang.translate('settings_clear_cache')); localStorage.clear(); } else { Noty.show(Lang.translate('settings_clear_cache_only')); var need = ['online_view', 'ser_clarifys', 'torrents_view', 'account_bookmarks', 'recomends_list', 'file_view', 'timetable', 'search_history', 'recomends_scan']; var more = ['online_', 'file_view_', 'storage_']; for (var key in localStorage) { if (more.find(function (w) { return key.indexOf(w) >= 0; })) need.push(key); } need.forEach(function (a) { localStorage.setItem(a, ''); }); } setTimeout(function () { window.location.reload(); }, 3000); } /** * Получить размер данных в localStorage * @doc * @name getsize * @alias Storage * @param {function} call функция обратного вызова */ function getsize(call) { if (localStorage) { var i = 0; var t = setInterval(function () { i += 250; try { localStorage.setItem('testsize', new Array(i * 1024 + 1).join('a')); } catch (e) { localStorage.removeItem('testsize'); clearInterval(t); } call((i - 250) * 1024); }, 100); } else { call(5000 * 1024); } } var Storage = { listener: listener, init: init$t, get: get$1, set: set, field: field, cache: cache, add: add$2, value: value, sync: sync, remove: remove, clear: clear$1, clean: clean, getsize: getsize, task: task }; var meta = { languages: { ru: { code: 'ru', name: 'Русский', lang_choice_title: 'Добро пожаловать', lang_choice_subtitle: 'Выберите свой язык' }, en: { code: 'en', name: 'English', lang_choice_title: 'Welcome', lang_choice_subtitle: 'Choose your language' }, uk: { code: 'uk', name: 'Українська', lang_choice_title: 'Ласкаво просимо', lang_choice_subtitle: 'Виберіть мову' }, be: { code: 'be', name: 'Беларуская', lang_choice_title: 'Сардэчна запрашаем', lang_choice_subtitle: 'Выберыце сваю мову' }, zh: { code: 'zh', name: '简体中文', lang_choice_title: '欢迎', lang_choice_subtitle: '选择你的语言' }, pt: { code: 'pt', name: 'Português', lang_choice_title: 'Bem-vindo', lang_choice_subtitle: 'Escolhe o teu idioma' }, bg: { code: 'bg', name: 'Български', lang_choice_title: 'Здравейте', lang_choice_subtitle: 'Изберете вашият език' }, he: { code: 'he', name: 'עִברִית', lang_choice_title: 'ברוך הבא', lang_choice_subtitle: 'בחר את השפה שלך' }, cs: { code: "cs", name: "Čeština", lang_choice_title: "Vítejte", lang_choice_subtitle: "Vyberte svůj jazyk" }, ro: { code: "ro", name: "Română", lang_choice_title: "Bun venit", lang_choice_subtitle: "Alegeți limba" }, fr: { code: 'fr', name: 'Français', lang_choice_title: 'Bienvenue', lang_choice_subtitle: 'Choisissez votre langue' }, pl: { code: "pl", name: "Polski", lang_choice_title: "Witamy", lang_choice_subtitle: "Wybierz język" }, es: { code: 'es', name: 'Español', lang_choice_title: 'Bienvenido', lang_choice_subtitle: 'Elige tu idioma' } } }; var ru = { lang_choice_title: 'Добро пожаловать', lang_choice_subtitle: 'Выберите свой язык', more: 'Еще', show_more: 'Показать ещё', more_results: 'Показать больше результатов', loading: 'Загрузка', nofind_movie: 'Не удалось найти фильм.', noname: 'Без названия', nochoice: 'Не выбрано', cancel: 'Отменить', confirm: 'Подтверждаю', sure: 'Вы уверены?', nodata: 'Нет данных', back: 'Назад', ready: 'Готово', close_app_modal: 'Вы уверены, что хотите покинуть приложение?', connected_seeds: 'подкл.', search: 'Поиск', search_input: 'Введите текст', search_empty: 'История поиска пуста.', search_delete: 'Влево - удалить', search_start_typing: 'Начните вводить текст для поиска.', search_searching: 'Идет поиск...', search_start: 'Начать поиск', search_nofound: 'По вашему запросу ничего не найдено.', global_search: 'Глобальный поиск', full_genre: 'Жанр', full_production: 'Производство', full_date_of_release: 'Дата релиза', full_budget: 'Бюджет', full_countries: 'Страны', full_like: 'Нравится', full_torrents: 'Торренты', full_trailers: 'Трейлеры', full_detail: 'Подробно', full_notext: 'Без описания.', full_series_release: 'Выход серий', full_next_episode: 'Следующая', full_episode_days_left: 'Осталось дней', full_trailer_official: 'Официальный', full_trailer_no_official: 'Неофициальный', full_season: 'Сезон', full_episode: 'Эпизод', full_directing: 'Режиссура', full_writing: 'Сценарий', full_keywords: 'Теги', full_crew: 'Еще', full_editing: 'Монтаж', full_creator: 'Создатель', settings_cub_sync: 'Синхронизация', settings_cub_sync_descr: 'Синхронизация с сервисом CUB: синхронизация ваших закладок, истории просмотров, меток и тайм-кодов.', settings_cub_account: 'Аккаунт', settings_cub_logged_in_as: 'Вошли как', settings_cub_profile: 'Профиль', settings_cub_sync_btn: 'Синхронизировать', settings_cub_sync_btn_descr: 'Сохранить локальные закладки в аккаунт CUB', settings_cub_backup: 'Бэкап', settings_cub_backup_descr: 'Сохранить или загрузить бэкап данных', settings_cub_logout: 'Выйти из аккаунта', settings_cub_signin: 'Авторизация', settings_cub_not_specified: 'Не указан', settings_cub_password: 'Пароль', settings_cub_status: 'Статус', settings_cub_backup_import: 'Импорт', settings_cub_backup_export: 'Экспорт', settings_cub_backup_export_descr: 'Сохранить настройки в облако', settings_cub_backup_import_descr: 'Восстановить настройки из облака', settings_cub_sync_filters: 'Синхронизация фильтров', settings_cub_sync_calendar: 'Синхронизация календаря', settings_cub_sync_quality: 'Синхронизация отметок (качество)', settings_cub_sync_search: 'Синхронизация истории поиска', settings_cub_sync_recomends: 'Синхронизация рекомендаций', settings_cub_sync_timecodes: 'Синхронизация тайм-кодов', settings_cub_signin_button: 'Выполнить вход', settings_cub_domain: 'Основной домен', settings_input_links: 'Избранное', settings_interface_type: 'Облегчённая версия', settings_interface_size: 'Размер интерфейса', settings_interface_menu_always: 'Всегда показывать левое меню', settings_interface_background: 'Фон', settings_interface_background_use: 'Показывать фон', settings_interface_background_type: 'Тип фона', settings_interface_performance: 'Быстродействие', settings_interface_animation: 'Анимация', settings_interface_advanced_animation: 'Продвинутая анимация', settings_interface_animation_descr: 'Анимация карточек и контента', settings_interface_attenuation: 'Затухание', settings_interface_attenuation_descr: 'Плавное затухание карточек снизу и сверху', settings_interface_scroll: 'Тип скроллинга', settings_interface_view_card: 'Тип просмотра карточек', settings_interface_view_card_descr: 'По мере скроллинга ленты карточки будут подгружаться постепенно или загружаться все', settings_interface_lang: 'Язык интерфейса', settings_interface_lang_reload: 'Необходимо перезапустить приложение, нажмите «OK» для перезагрузки', settings_interface_card_interfice: 'Интерфейс карточек', settings_interface_card_poster: 'Показать постер', settings_interface_card_cover: 'Показать обложку', settings_interface_card_reactions: 'Показать реакции', settings_interface_glass: 'Стекло', settings_interface_glass_descr: 'Показывать интерфейс в стекловидном стиле', settings_interface_glass_opacity: 'Прозрачность стекла', settings_interface_black_style: 'Чёрный стиль', settings_interface_hide_outside_the_screen: 'Скрывать карточки за пределами экрана', settings_interface_hide_outside_the_screen_descr: 'Это ускорит рендер интерфейса и улучшит производительность', settings_interface_sound: 'Системные звуки', settings_interface_sound_play: 'Переходы', settings_interface_sound_level: 'Уровень громкости', settings_main_account: 'Аккаунт', settings_main_interface: 'Интерфейс', settings_main_player: 'Плеер', settings_main_parser: 'Парсер', settings_main_torrserver: 'TorrServer', settings_main_plugins: 'Расширения', settings_main_rest: 'Остальное', settings_rest_start: 'Стартовая страница', settings_rest_start_descr: 'С какой страницы начинать при запуске', settings_rest_source: 'Источник', settings_rest_source_use: 'Основной источник', settings_rest_source_descr: 'Откуда брать информацию о фильмах', settings_rest_tmdb_lang: 'На каком языке отображать данные с TMDB', settings_rest_tmdb_prox: 'Проксировать TMDB', settings_rest_tmdb_prox_auto: 'Включить прокси автоматически', settings_rest_tmdb_posters: 'Разрешение постеров TMDB', settings_rest_screensaver: 'Скринсейвер', settings_rest_screensaver_use: 'Показывать заставку при бездействии', settings_rest_screensaver_type: 'Тип заставки', settings_rest_screensaver_time: 'Через сколько минут запустить скринсейвер', settings_rest_helper: 'Подсказки', settings_rest_helper_use: 'Показывать подсказки', settings_rest_helper_reset: 'Показать подсказки снова', settings_rest_pages: 'Сколько страниц хранить в памяти', settings_rest_pages_descr: 'Хранит страницы в том состоянии, в котором вы их покинули.', settings_rest_time: 'Сместить время', settings_rest_navigation: 'Тип навигации', settings_rest_keyboard: 'Тип клавиатуры', settings_rest_device: 'Название устройства', settings_rest_device_placeholder: 'Например: Моя Лампа', settings_rest_cache: 'Очистить кэш', settings_rest_tmdb_example: 'Например:', settings_rest_tmdb_api_descr: 'Для получения данных', settings_rest_tmdb_image_descr: 'Для получения изображений', settings_rest_card_quality: 'Отметки качества', settings_rest_card_quality_descr: 'Отображать отметки качества на карточках', settings_rest_card_episodes: 'Отметки эпизодов', settings_rest_card_episodes_descr: 'Отображать отметки эпизодов на карточках', settings_rest_cache_images: 'Кэш изображений', settings_rest_cache_images_descr: 'Кэшировать постеры и фоны в локальное хранилище', settings_rest_protocol_use: 'Безопасное соединение', settings_rest_protocol_descr: 'Использовать протокол HTTPS', settings_rest_cache_only: 'Только кеш', settings_rest_cache_only_descr: 'Основные настройки сохранятся', settings_rest_cache_all: 'Кеш и данные', settings_rest_cache_all_descr: 'Будут очищены все настройки и данные', settings_rest_cache_calculate: 'Сколько осталось памяти', settings_request_caching_use: 'Кэширование запросов', settings_request_caching_descr: 'Сохранять ответы от серверов в кэш для ускорения работы приложения', settings_parser_use: 'Использовать парсер', settings_parser_use_descr: 'Тем самым, вы соглашаетесь принять на себя всю ответственность за использование публичных ссылок для просмотра торрент и онлайн контента.', settings_parser_type: 'Тип парсера для торрентов', settings_parser_jackett_placeholder: 'Например: 192.168.х', settings_parser_jackett_link: 'Ссылка', settings_parser_jackett_link_descr: 'Укажите ссылку на скрипт Jackett', settings_parser_prowlarr_link_descr: 'Укажите ссылку на сервер Prowlarr', settings_parser_jackett_key_placeholder: 'Например: sa0sk83d..', settings_parser_jackett_key: 'Api-ключ', settings_parser_jackett_key_descr: 'Находится в Jackett', settings_parser_prowlarr_key_descr: 'Находится во вкладке настроек Prowlarr в категории основное', settings_parser_torlook_type: 'Метод парсинга сайта TorLook', settings_parser_scraperapi_placeholder: 'Например: scraperapi.com', settings_parser_scraperapi_link: 'Ссылка на парсер сайтов', settings_parser_scraperapi_descr: 'Зарегистрируйтесь на сайте scraperapi.com, введите ссылку api.scraperapi.com?api_key=...&url={q}
    В {q} будет поставляться сайт w41.torlook.info', settings_parser_search: 'Поиск', settings_parser_search_descr: 'На каком языке производить поиск?', settings_parser_in_search: 'Парсер в поиске', settings_parser_in_search_descr: 'Показывать результаты в поиске?', settings_parser_timeout_title: 'Таймаут парсера', settings_parser_timeout_descr: 'Время (в секундах) ожидания ответа от сервера', settings_player_type: 'Тип плеера', settings_player_type_descr: 'Каким плеером воспроизводить видео', settings_infuse_launch_mode: 'Запуск в Infuse', settings_infuse_launch_mode_descr: 'Что делать при открытии видео в Infuse. Для торрентов всегда сразу воспроизведение', settings_infuse_launch_ask: 'Спрашивать при запуске', settings_infuse_launch_play: 'Играть', settings_infuse_launch_save_and_play: 'Сохранить и играть', settings_player_iptv_type: 'Тип плеера для IPTV', settings_player_iptv_type_descr: 'Каким плеером воспроизводить IPTV-каналы', settings_player_torrent_type: 'Тип плеера для торрентов', settings_player_torrent_type_descr: 'Каким плеером воспроизводить торренты', settings_player_reset: 'Сбросить плеер по умолчанию', settings_player_reset_descr: 'Сбрасывает выбранный Android-плеер в приложении', settings_player_path: 'Путь к плееру', settings_player_path_descr: 'Укажите путь к исполняемому файлу плеера', settings_player_external_header: 'Внешний плеер', settings_player_external_fullscreen: 'Запускать в полноэкранном режиме', settings_player_normalization: 'Нормализация звука', settings_player_normalization_descr: 'Нормализирует звук в один уровень, понижает громкие звуки и повышает тихие.', settings_player_next_episode: 'Следующая серия', settings_player_next_episode_descr: 'Автоматически переключать на следующую серию по окончании текущей', settings_player_timecode: 'Тайм-код', settings_player_timecode_descr: 'Продолжить с последнего места просмотра', settings_player_scale: 'Метод масштабирования', settings_player_scale_descr: 'Каким образом производить вычисления для масштабирования видео', settings_player_subs: 'Субтитры', settings_player_subs_use: 'Включить', settings_player_subs_use_descr: 'Всегда включать субтитры после запуска видео', settings_player_subs_lang: 'Язык субтитров', settings_player_subs_lang_descr: 'Какие субтитры включать автоматически, если доступно несколько языков', settings_param_subtitles_lang_auto: 'Язык интерфейса', settings_player_subs_size: 'Размер', settings_player_subs_size_descr: 'Размер субтитров на экране', settings_player_subs_stroke_use: 'Использовать окантовку', settings_player_subs_stroke_use_descr: 'Субтитры будут обведены черным цветом для улучшения читаемости', settings_player_subs_backdrop_use: 'Использовать подложку', settings_player_subs_backdrop_use_descr: 'Субтитры будут отображаться на полупрозрачной подложке для улучшения читаемости', settings_player_quality: 'Качество видео по умолчанию', settings_player_quality_descr: 'Предпочтительное качество видео для просмотра', settings_player_hls_title: 'Обработка потока .m3u8', settings_player_hls_descr: 'Не трогайте этот параметр, если не знаете, зачем он.', settings_player_rewind_title: 'Перемотка', settings_player_rewind_descr: 'Интервал перемотки в секундах', settings_player_launch_trailers: 'Плеер для трейлеров', settings_plugins_notice: 'Для применения плагина необходимо перезагрузить приложение', settings_plugins_add: 'Добавить плагин', settings_plugins_add_descr: 'Для удаления добавленного плагина удерживайте или нажмите дважды клавишу (OK) на нем', settings_plugins_install: 'Установить плагин', settings_plugins_install_descr: 'Установить плагин из списка доступных', settings_server_link: 'Использовать ссылку', settings_server_links: 'Ссылки', settings_server_placeholder: 'Например: 192.168.х', settings_server_link_one: 'Основная ссылка', settings_server_link_one_descr: 'Укажите основную ссылку на TorrServer', settings_server_link_two: 'Дополнительная ссылка', settings_server_link_two_descr: 'Укажите дополнительную ссылку на TorrServer', settings_server_additionally: 'Дополнительно', settings_server_client: 'Встроенный клиент', settings_server_client_descr: 'Использовать встроенный JS-клиент TorrServer, иначе запускается системный.', settings_server_base: 'Сохранять в базу', settings_server_base_descr: 'Торрент будет добавлен в базу TorrServer', settings_server_preload: 'Использовать буфер пред.загрузки', settings_server_preload_descr: 'Дожидаться заполнения буфера предварительной загрузки TorrServer перед проигрыванием', settings_server_auth: 'Авторизация', settings_server_password_use: 'Вход по паролю', settings_server_login: 'Логин', settings_server_password: 'Пароль', settings_server_not_specified: 'Не указан', settings_server_search_type: 'Тип поиска', settings_server_search_type_descr: 'Где искать торренты: встроенный rutor, Torznab-индексеры в TorrServer, или оба источника.', settings_param_torrserver_search_rutor: 'Rutor', settings_param_torrserver_search_torznab: 'Torznab', settings_param_torrserver_search_both: 'Rutor + Torznab', settings_server_gts: 'Транскодирование', settings_server_gts_descr: 'Автоматически преобразовывать видео в совместимый формат при необходимости.', settings_server_tracktimecode: 'Включить TrackTimecode', settings_server_tracktimecode_descr: 'Сохраняет прогресс просмотра фильмов и сериалов на сервере TorrServer. Для работы эту опцию также необходимо включить в TorrServer.', settings_webos_launcher: 'Запуск приложения', settings_webos_launcher_add_device: 'Установить как стартовое', settings_webos_launcher_remove_device: 'Убрать из стартовых приложений', settings_parental_control_param_time_always: 'Всегда', settings_parental_control_param_time_once: 'Один раз', settings_parental_control_param_time_10: 'Через 10 минут', settings_parental_control_param_time_20: 'Через 20 минут', settings_parental_control_param_time_30: 'Через 30 минут', settings_parental_control_param_time_60: 'Через 1 час', settings_parental_control_param_time_120: 'Через 2 часа', settings_parental_control_change_pin: 'Изменить PIN-код', settings_parental_control_demand_title: 'Когда запрашивать', settings_parental_control_demand_descr: 'Через какое время запрашивать PIN-код', settings_parental_control_enabled: 'Включено', settings_parental_control_disabled: 'Отключено', settings_parental_control_where: 'Где запрашивать PIN-код', settings_lgbt_content_block: 'Блокировать ЛГБТ контент', parental_control_input_new_code: 'Введите новый PIN-код', parental_control_confirm_new_code: 'Повторите PIN-код', parental_control_no_match_code: 'PIN-код не совпадает с прошлым кодом', parental_control_input_code: 'Введите PIN-код', parental_control_input_error: 'Вы ввели неправильный PIN-код', torent_nohash_reasons: 'Причины', torent_nohash_reason_one: 'TorServer не смог скачать торрент файл', torent_nohash_reason_two: 'Ответ от TorServer', torent_nohash_reason_three: 'Ссылка', torent_nohash_do: 'Что делать?', torent_nohash_do_one: 'Проверьте правильно ли вы настроили Jackett', torent_nohash_do_two: 'Приватные источники могут не выдавать ссылку на файл', torent_nohash_do_three: 'Убедитесь что Jackett тоже может скачать файл', torent_nohash_do_four: 'Написать в нашу Telegram-группу: @lampa_group', torent_nohash_do_five: 'Укажите какой фильм, какая раздача и по возможности фото этой раздачи', torrent_error_text: 'Не удалось подключиться к TorrServer. Давайте быстро пройдёмся по списку возможных проблем и всё проверим.', torrent_error_step_1: 'Запущен ли TorrServer', torrent_error_step_2: 'Динамический IP-адрес', torrent_error_step_3: 'Протокол и порт', torrent_error_step_4: 'Блокировка антивирусами', torrent_error_step_5: 'Проверьте на доступность', torrent_error_step_6: 'Все равно не работает', torrent_error_info_1: 'Убедитесь, что вы запустили TorrServer на устройстве, где он установлен.', torrent_error_info_2: 'Частая ошибка, изменился IP-адрес устройства с TorrServer. Убедитесь, что IP-адрес, который вы ввели - {ip}, совпадает с адресом устройства, на котором установлен TorrServer.', torrent_error_info_3: 'Для подключения к TorrServer, необходимо указать протокол http:// в начале и порт :8090 в конце адреса. Убедитесь, что после IP-адреса указан порт, ваш текущий адрес - {ip}', torrent_error_info_4: 'Частое явление, антивирус или брандмауэр может блокировать доступ по IP-адресу, попробуйте отключить антивирус и брандмауэр.', torrent_error_info_5: 'На любом другом устройстве в этой же сети, откройте в браузере адрес {ip} и проверьте, доступен ли веб-интерфейс TorrServer.', torrent_error_info_6: 'Если после всех проверок всё равно возникает ошибка подключения, попробуйте перезагрузить TorrServer и интернет-адаптер.', torrent_error_info_7: 'Если проблема не устранена, пишите в Telegram-группу @lampa_group с текстом (Lampa не подключается к TorrServer после всех проверок, текущий адрес {ip})', torrent_error_start: 'Начать проверку', torrent_error_nomatrix: 'Не удалось подтвердить версию Matrix', torrent_error_made: 'Выполнено', torrent_error_from: 'из', torrent_error_next: 'Далее', torrent_error_complite: 'Завершить', torrent_error_connect: 'Ошибка подключения', torrent_error_check_no_auth: 'Сервер ответил на запрос, но не удалось пройти авторизацию', torrent_install_need: 'Необходим TorrServer', torrent_install_text: 'TorrServer – приложение, которое позволяет просматривать контент из торрент-файлов в онлайн режиме.

    Более детальную информацию по установке вы найдете в Telegram-группах, указанных ниже.', torrent_install_contact: 'Telegram-группы', torrent_item_bitrate: 'Битрейт', torrent_item_seeds: 'Раздают', torrent_item_grabs: 'Качают', torrent_item_mb: 'Мбит/с', torrent_serial_episode: 'Серия', torrent_serial_season: 'Сезон', torrent_serial_date: 'Выход', torrent_get_magnet: 'Запрашиваю magnet ссылку', torrent_remove_title: 'Удалить', torrent_remove_descr: 'Торрент будет удален из вашего списка', torrent_parser_any_one: 'Любое', torrent_parser_any_two: 'Любой', torrent_parser_no_choice: 'Не выбрано', torrent_parser_yes: 'Да', torrent_parser_no: 'Нет', torrent_parser_quality: 'Качество', torrent_parser_subs: 'Субтитры', torrent_parser_voice: 'Перевод', torrent_parser_tracker: 'Трекер', torrent_parser_year: 'Год', torrent_parser_season: 'Сезон', torrent_parser_sort_by_seeders: 'По раздающим', torrent_parser_sort_by_size: 'По размеру', torrent_parser_sort_by_name: 'По названию', torrent_parser_sort_by_tracker: 'По источнику', torrent_parser_sort_by_date: 'По дате', torrent_parser_sort_by_viewed: 'По просмотренным', torrent_parser_voice_dubbing: 'Дубляж', torrent_parser_voice_polyphonic: 'Многоголосый', torrent_parser_voice_two: 'Двухголосый', torrent_parser_voice_amateur: 'Любительский', torrent_parser_reset: 'Сбросить фильтр', torrent_parser_empty: 'Не удалось получить результатов', torrent_parser_no_hash: 'Не удалось получить HASH, попробуйте перезагрузить TorrServer', torrent_parser_added_to_mytorrents: 'добавлено в «Мои торренты»', torrent_parser_add_to_mytorrents: 'Добавить в «Мои торренты»', torrent_parser_label_title: 'Пометить', torrent_parser_label_descr: 'Пометить раздачу с флагом (просмотрено)', torrent_parser_label_cancel_title: 'Снять отметку', torrent_parser_label_cancel_descr: 'Снять отметку с раздачи (просмотрено)', torrent_parser_timeout: 'Время ожидания истекло', torrent_parser_nofiles: 'Не удалось извлечь подходящие файлы', torrent_parser_set_link: 'Укажите ссылку для парсинга', torrent_parser_request_error: 'Ошибка в запросе', torrent_parser_magnet_error: 'Не удалось получить magnet ссылку', torrent_parser_no_responce: 'Парсер не отвечает на запрос', torrent_parser_torlook_fallback_search_notification: 'Парсер Jackett не доступен или настройки подключения не верны. Осуществляется поиск в Torlook...', about_text: 'Приложение полностью бесплатное и использует публичные ссылки для получения информации о видео, новинках, популярных фильмах и т.д. Вся доступная информация используется исключительно в познавательных целях, приложение не использует свои собственные серверы для распространения информации.', about_channel: 'Наш канал', about_group: 'Группа', about_version: 'Версия', about_donate: 'Донат', title_watched: 'Вы смотрели', title_settings: 'Настройки', title_collections: 'Подборки', title_company: 'Компания', title_actors: 'Актеры', title_actor: 'Актер', title_actress: 'Актриса', title_person: 'Персона', title_persons: 'Персоны', title_person_about: 'О персоне', title_comments: 'Комментарии', title_torrents: 'Торренты', title_trailers: 'Трейлеры', title_watch: 'Смотреть', title_error: 'Ошибка', title_links: 'Ссылки', title_choice: 'Выбрать', title_main: 'Главная', title_book: 'Закладки', title_like: 'Нравится', title_wath: 'Позже', title_look: 'Смотрю', title_scheduled: 'Запланировано', title_viewed: 'Просмотрено', title_thrown: 'Брошено', title_continued: 'Продолжение следует', title_history: 'История просмотров', title_mytorrents: 'Мои торренты', title_last: 'Последняя', title_action: 'Действие', title_action_infuse: 'Действие Infuse', title_producer: 'Режиссер', title_collection: 'Коллекция', title_recomendations: 'Рекомендации', title_similar: 'Похожие', title_about: 'О приложении', title_timetable: 'Расписание', title_relises: 'Цифровые релизы', title_catalog: 'Каталог', title_category: 'Категория', title_parser: 'Парсер', title_type: 'Тип', title_rating: 'Рейтинг', title_country: 'Страна', title_year: 'Год', title_genre: 'Жанр', title_filter: 'Фильтр', title_notice: 'Уведомления', title_files: 'Файлы', title_now_watch: 'Сейчас смотрят', title_latest: 'Последнее добавление', title_continue: 'Продолжить просмотр', title_recomend_watch: 'Рекомендуем посмотреть', title_new_episodes: 'Новые серии', title_popular: 'Популярное', title_popular_movie: 'Популярные фильмы', title_popular_tv: 'Популярные сериалы', title_new_this_year: 'Новинки этого года', title_hight_voite: 'С высоким рейтингом', title_new: 'Новинки', title_trend_day: 'Сегодня в тренде', title_trend_week: 'В тренде за неделю', title_upcoming: 'Смотрите в кинозалах', title_top_movie: 'Топ фильмы', title_top_tv: 'Топ сериалы', title_tv_today: 'Сегодня в эфире', title_this_week: 'На этой неделе', title_in_top: 'В топе', title_out: 'Выход', title_out_confirm: 'Да, выйти', title_continue_two: 'Продолжить', title_choice_language: 'Выбрать язык', title_subscribe: 'Подписаться', title_subscribes: 'Подписки', title_unsubscribe: 'Отписаться', title_language: 'Язык оригинала', title_language_short: 'Язык', title_ongoing: 'Онгоинги', title_pgrating: 'Возрастное ограничение', title_card: 'Карточка', title_seasons: 'Сезоны', title_episodes: 'Серии', title_rewiews: 'Отзывы', title_channel: 'Канал', title_in_high_quality: 'В высоком качестве', title_author: 'Автор', title_buffer: 'Буфер', title_upcoming_episodes: 'Ближайшие выходы эпизодов', title_recent_episodes: 'Недавние выходы эпизодов', title_reactions: 'Реакции', title_last_year: 'Прошлый год', title_reset: 'Перезагрузить', title_left: 'Осталось', title_parental_control: 'Родительский контроль', title_worth_rewatch: 'Стоит пересмотреть', title_fire: 'Огонь!', title_top_100: 'Топ 100', title_ai_assistant: 'AI-ассистент', title_facts: 'Интересные факты', title_anons: 'Анонс', title_developer: 'Разработчик', title_navigation: 'Навигация', title_page: 'Страница', title_channels: 'Каналы', title_best_of_2000: 'Золотые 2000-е', title_best_of_2010: 'Хиты 2010-х', title_best_of_2015: 'Современная классика', title_comedy_of_2000: 'Смех 2000-х', title_comedy_of_2010: 'Юмор 2010-х', title_comedy_of_2015: 'Современные комедии', title_metadata: 'Метаданные', title_moods: 'Настроения', title_meta_violence: 'Насилие', title_meta_fear: 'Страх', title_meta_profanity: 'Лексика', title_meta_sadness: 'Грусть', title_meta_sex: 'Сексуальность', title_meta_humor: 'Юмор', title_meta_tension: 'Напряжение', title_meta_romance: 'Романтика', title_meta_pace: 'Темп', title_meta_importance: 'Важные сцены', title_meta_action: 'Экшн', reactions_none: 'Нет реакций', reactions_fire: 'Супер', reactions_nice: 'Неплохо', reactions_think: 'Смотрибельно', reactions_bore: 'Скука', reactions_shit: 'Плохо', reactions_ready: 'Вы уже оставили реакцию', subscribe_success: 'Вы успешно подписались', subscribe_error: 'Возникла ошибка при подписке, попробуйте позже', subscribe_noinfo: 'Не удалось получить информацию, попробуйте позже', subscribe_info: 'В разделе (Подписки) у вас будут отображаться все ваши подписки на переводы. Когда выйдет новая серия с выбранным переводом, вы получите уведомление.', company_headquarters: 'Штаб', company_homepage: 'Сайт', company_country: 'Страна', country_ad: 'Андорра', country_ae: 'ОАЭ', country_af: 'Афганистан', country_al: 'Албания', country_am: 'Армения', country_ao: 'Ангола', country_ar: 'Аргентина', country_at: 'Австрия', country_au: 'Австралия', country_aw: 'Аруба', country_az: 'Азербайджан', country_bа: 'Босния и Герцеговина', country_bd: 'Бангладеш', country_be: 'Бельгия', country_bg: 'Болгария', country_bh: 'Бахрейн', country_bi: 'Бурунди', country_bj: 'Бенин', country_bo: 'Боливия', country_br: 'Бразилия', country_bs: 'Багамские о-ва', country_bt: 'Бутан', country_bw: 'Ботсвана', country_by: 'Беларусь', country_ca: 'Канада', country_ch: 'Швейцария', country_cl: 'Чили', country_cm: 'Камерун', country_cn: 'Китай', country_co: 'Колумбия', country_cr: 'Коста-Рика', country_cu: 'Куба', country_cv: 'Кабо-Верде', country_cy: 'Кипр', country_cz: 'Чехия', country_de: 'Германия', country_dj: 'Джибути', country_dk: 'Дания', country_do: 'Доминикана', country_dz: 'Алжир', country_ec: 'Эквадор', country_ee: 'Эстония', country_eg: 'Египет', country_es: 'Испания', country_et: 'Эфиопия', country_fi: 'Финляндия', country_fo: 'Фарерские о-ва', country_fr: 'Франция', country_ga: 'Габон', country_gb: 'Великобритания', country_ge: 'Грузия', country_gh: 'Гана', country_gl: 'Гренландия', country_gp: 'Гваделупа', country_gr: 'Греция', country_gt: 'Гватемала', country_hk: 'Гонконг', country_hr: 'Хорватия', country_ht: 'Гаити', country_hu: 'Венгрия', country_id: 'Индонезия', country_ie: 'Ирландия', country_il: 'Израиль', country_in: 'Индия', country_iq: 'Ирак', country_ir: 'Иран', country_is: 'Исландия', country_it: 'Италия', country_jm: 'Ямайка', country_jo: 'Иордания', country_jp: 'Япония', country_ke: 'Кения', country_kg: 'Киргизия', country_kh: 'Камбоджа', country_kp: 'Северная Корея', country_kr: 'Южная Корея', country_kz: 'Казахстан', country_kw: 'Кувейт', country_la: 'Лаос', country_lb: 'Ливан', country_li: 'Лихтенштейн', country_lk: 'Шри-Ланка', country_lr: 'Либерия', country_lt: 'Литва', country_lu: 'Люксембург', country_lv: 'Латвия', country_ly: 'Ливия', country_ma: 'Марокко', country_mc: 'Монако', country_md: 'Молдова', country_me: 'Черногория', country_mk: 'Македония', country_mm: 'Мьянма', country_mn: 'Монголия', country_mo: 'Макао', country_mt: 'Мальта', country_mu: 'Маврикий', country_mv: 'Мальдивы', country_mw: 'Малави', country_mx: 'Мексика', country_my: 'Малайзия', country_mz: 'Мозамбик', country_na: 'Намибия', country_ne: 'Нигер', country_ng: 'Нигерия', country_ni: 'Никарагуа', country_nl: 'Нидерланды', country_no: 'Норвегия', country_np: 'Непал', country_nz: 'Новая Зеландия', country_om: 'Оман', country_pa: 'Панама', country_pe: 'Перу', country_pg: 'Папуа - Новая Гвинея', country_ph: 'Филиппины', country_pk: 'Пакистан', country_pl: 'Польша', country_pr: 'Пуэрто-Рико', country_ps: 'Палестина', country_pt: 'Португалия', country_py: 'Парагвай', country_qa: 'Катар', country_ro: 'Румыния', country_rs: 'Сербия', country_ru: 'Россия', country_rw: 'Руанда', country_sa: 'Саудовская Аравия', country_sd: 'Судан', country_se: 'Швеция', country_sg: 'Сингапур', country_si: 'Словения', country_sk: 'Словакия', country_sn: 'Сенегал', country_su: 'СССР', country_sv: 'Сальвадор', country_sy: 'Сирия', country_th: 'Таиланд', country_tj: 'Таджикистан', country_tm: 'Туркменистан', country_tn: 'Тунис', country_tr: 'Турция', country_tw: 'Тайвань', country_tz: 'Танзания', country_ua: 'Украина', country_ug: 'Уганда', country_us: 'США', country_uy: 'Уругвай', country_uz: 'Узбекистан', country_ve: 'Венесуэла', country_vn: 'Вьетнам', country_ws: 'Самоа', country_xk: 'Косово', country_ye: 'Йемен', country_yu: 'Югославия', country_za: 'ЮАР', country_zm: 'Замбия', country_zw: 'Зимбабве', filter_clarify: 'Уточнить', filter_clarify_two: 'Уточнить поиск', filter_set_name: 'Указать название', filter_sorted: 'Сортировать', filter_filtred: 'Фильтр', filter_any: 'Любой', filter_combinations: 'Комбинации', filter_alt_names: 'Другие названия', filter_rating_from: 'от', filter_rating_to: 'до', filter_lang_af: 'Африкаанс', filter_lang_ar: 'Арабский', filter_lang_az: 'Азербайджанский', filter_lang_ba: 'Башкирский', filter_lang_be: 'Белорусский', filter_lang_bg: 'Болгарский', filter_lang_bn: 'Бенгальский', filter_lang_bs: 'Боснийский', filter_lang_ca: 'Каталанский', filter_lang_ce: 'Чеченский', filter_lang_cs: 'Чешский', filter_lang_da: 'Датский', filter_lang_de: 'Немецкий', filter_lang_el: 'Греческий', filter_lang_en: 'Английский', filter_lang_es: 'Испанский', filter_lang_et: 'Эстонский', filter_lang_fa: 'Персидский', filter_lang_fi: 'Финский', filter_lang_fr: 'Французский', filter_lang_ga: 'Ирландский', filter_lang_gl: 'Галисийский', filter_lang_gn: 'Гуарани', filter_lang_he: 'Иврит', filter_lang_hi: 'Хинди', filter_lang_hr: 'Хорватский', filter_lang_hu: 'Венгерский', filter_lang_hy: 'Армянский', filter_lang_id: 'Индонезийский', filter_lang_is: 'Исландский', filter_lang_it: 'Итальянский', filter_lang_ja: 'Японский', filter_lang_ka: 'Грузинский', filter_lang_kk: 'Казахский', filter_lang_ko: 'Корейский', filter_lang_ks: 'Кашмири', filter_lang_ku: 'Курдский', filter_lang_ky: 'Киргизский', filter_lang_lt: 'Литовский', filter_lang_lv: 'Латышский', filter_lang_mi: 'Маори', filter_lang_mk: 'Македонский', filter_lang_mn: 'Монгольский', filter_lang_mt: 'Мальтийский', filter_lang_ne: 'Непальский', filter_lang_nl: 'Нидерландский', filter_lang_no: 'Норвежский', filter_lang_pa: 'Панджаби', filter_lang_pl: 'Польский', filter_lang_ps: 'Пушту', filter_lang_pt: 'Португальский', filter_lang_ro: 'Румынский', filter_lang_ru: 'Русский', filter_lang_si: 'Сингальский', filter_lang_sk: 'Словацкий', filter_lang_sl: 'Словенский', filter_lang_sm: 'Самоанский', filter_lang_so: 'Сомалийский', filter_lang_sq: 'Албанский', filter_lang_sr: 'Сербский', filter_lang_sv: 'Шведский', filter_lang_sw: 'Суахили', filter_lang_ta: 'Тамильский', filter_lang_tg: 'Таджикский', filter_lang_th: 'Тайский', filter_lang_tk: 'Туркменский', filter_lang_tr: 'Турецкий', filter_lang_tt: 'Татарский', filter_lang_ur: 'Урду', filter_lang_uk: 'Украинский', filter_lang_uz: 'Узбекский', filter_lang_vi: 'Вьетнамский', filter_lang_yi: 'Идиш', filter_lang_zh: 'Китайский', filter_genre_ac: 'Боевик', filter_genre_ad: 'Приключения', filter_genre_mv: 'Мультфильм', filter_genre_cm: 'Комедия', filter_genre_cr: 'Криминал', filter_genre_dc: 'Документальный', filter_genre_dr: 'Драма', filter_genre_fm: 'Семейный', filter_genre_fe: 'Фэнтези', filter_genre_hi: 'История', filter_genre_ho: 'Ужасы', filter_genre_mu: 'Музыка', filter_genre_de: 'Детектив', filter_genre_md: 'Мелодрама', filter_genre_fa: 'Фантастика', filter_genre_tv: 'Телевизионный фильм', filter_genre_tr: 'Триллер', filter_genre_mi: 'Военный', filter_genre_ve: 'Вестерн', filter_genre_aa: 'Боевик и Приключения', filter_genre_ch: 'Детский', filter_genre_nw: 'Новости', filter_genre_rs: 'Реалити-шоу', filter_genre_hf: 'НФ и Фэнтези', filter_genre_op: 'Мыльная опера', filter_genre_tc: 'Ток-шоу', filter_genre_mp: 'Война и Политика', filter_keyword_sequel: 'Сиквел', filter_keyword_based_comic: 'По комиксам', filter_keyword_space: 'Космос', filter_keyword_survival: 'Выживание', filter_keyword_zombie: 'Зомби', filter_keyword_school: 'Школа', filter_keyword_revenge: 'Месть', filter_keyword_detective: 'Детектив', filter_keyword_robots: 'Роботы', filter_keyword_vampires: 'Вампиры', filter_keyword_aliens: 'Инопланетяне', filter_keyword_police: 'Полиция', filter_keyword_reincarnation: 'Перерождение', filter_keyword_spy: 'Шпион', filter_keyword_disaster: 'Катастрофа', filter_keyword_christmas: 'Рождество', empty_title: 'Пусто', empty_text: 'По вашему фильтру ничего не нашлось, уточните фильтр.', empty_title_two: 'Здесь пусто', empty_text_two: 'На данный момент список пустой', empty_subscribes_title: 'Ваши подписки на переводы', empty_subscribes_text: 'Просто подпишитесь на любимый перевод и мы будем уведомлять вас о выходе новой серии.', empty_history_title: 'Ваша история просмотров', empty_history_text: 'Здесь будет отображаться ваша история просмотров.', empty_bookmark_title: 'Ваше избранное', empty_bookmark_text: 'Добавляйте в избранное понравившиеся фильмы и сериалы, чтобы быстро находить их в этом разделе.', empty_mytorrents_title: 'Ваши торренты', empty_mytorrents_text: 'Здесь будут отображаться загруженные вами торренты через TorServer.', back_to_card: 'Вернуться в карточку', menu_main: 'Главная', menu_movies: 'Фильмы', menu_tv: 'Сериалы', menu_catalog: 'Каталог', menu_filter: 'Фильтр', menu_collections: 'Подборки', menu_relises: 'Релизы', menu_anime: 'Аниме', menu_bookmark: 'Закладки', menu_like: 'Нравится', menu_time: 'Позже', menu_history: 'История', menu_timeline: 'Расписание', menu_torrents: 'Торренты', menu_settings: 'Настройки', menu_about: 'Информация', menu_console: 'Консоль', menu_multmovie: 'Мультфильмы', menu_multtv: 'Мультсериалы', menu_feed: 'Лента', lampa_movie_title: 'Ламповое кино', lampa_movie_descr: 'Следите за новинками на своем смартфоне.
    Телеграм: @lampa_movie', plugins_catalog_work: 'Рабочие плагины', plugins_catalog_work_descr: 'Плагины, которые точно работают в лампе.', plugins_catalog_popular: 'Популярные плагины среди пользователей', plugins_catalog_popular_descr: 'Установка из неизвестных источников может привести к некорректной работе приложения.', plugins_online: 'Просмотр онлайн', plugins_check_fail: 'Не удалось проверить работоспособность плагина. Однако это не означает, что плагин не работает. Перезагрузите приложение для выяснения, загружается ли плагин.', plugins_need_reload: 'Для применения плагина необходимо перезагрузить приложение', plugins_install: 'Установить', plugins_install_ready: 'Этот плагин уже установлен.', plugins_installed: 'Установок', plugins_load_from: 'Загружено из CUB', plugins_ok_for_check: 'Нажмите (OK) для проверки плагина', plugins_no_loaded: 'При загрузке приложения, часть плагинов не удалось загрузить', plugins_remove: 'Удалить плагины', plugins_add_success: 'Плагин успешно подключен', time_viewed: 'Просмотрено', time_from: 'из', time_reset: 'Сбросить тайм-код', settings_clear_cache: 'Кеш и данные очищены', settings_clear_cache_only: 'Кеш очищен', settings_user_links: 'Пользовательская ссылка', settings_for_local: 'Для локального TorrServer', settings_add: 'Добавить', settings_remove: 'Удалить', settings_this_value: 'текущее значение', settings_added: 'Добавлено', settings_removed: 'Удалено', settings_reset: 'Сброс настроек', settings_param_player_inner: 'Встроенный', settings_param_player_outside: 'Внешний', settings_param_player_inner_tvos: 'Встроенный', settings_param_yes: 'Да', settings_param_no: 'Нет', settings_param_interface_size_small: 'Меньше', settings_param_interface_size_normal: 'Нормальный', settings_param_interface_size_bigger: 'Больше', settings_param_poster_quality_low: 'Низкое', settings_param_poster_quality_average: 'Среднее', settings_param_poster_quality_high: 'Высокое', settings_param_parse_directly: 'Напрямую', settings_param_parse_api: 'Через API сайта', settings_param_background_complex: 'Сложный', settings_param_background_simple: 'Простой', settings_param_background_image: 'Картинка', settings_param_link_use_one: 'Основную', settings_param_link_use_two: 'Дополнительную', settings_param_link_use_both: 'Обе', settings_param_subtitles_size_small: 'Маленькие', settings_param_subtitles_size_normal: 'Обычные', settings_param_subtitles_size_bigger: 'Большие', settings_param_screensaver_nature: 'Природа', settings_param_torrent_lang_orig: 'Оригинал', settings_param_player_timecode_again: 'Начать с начала', settings_param_player_timecode_continue: 'Продолжить', settings_param_player_timecode_ask: 'Спрашивать', settings_param_player_scale_method: 'Рассчитать', settings_param_player_hls_app: 'Системный', settings_param_player_hls_js: 'Программный', settings_param_card_view_load: 'Подгружать', settings_param_card_view_all: 'Показать все', settings_param_navigation_remote: 'Пульт', settings_param_navigation_mouse: 'Пульт с мышкой', settings_param_navigation_touch: 'Сенсор', settings_param_keyboard_lampa: 'Встроенная', settings_param_keyboard_system: 'Системная', settings_param_card_interface_old: 'Старый', settings_param_card_interface_new: 'Новый', settings_param_glass_easy: 'Прозрачная', settings_param_glass_medium: 'Полупрозрачная', settings_param_glass_blacked: 'Затемнённая', settings_param_jackett_interview_all: 'Все', settings_param_jackett_interview_healthy: 'Только доступные', settings_parser_jackett_interview: 'Опрашивать трекеры', helper_keyboard: 'После ввода значения нажмите кнопку «Назад» для сохранения', helper_torrents: 'Удерживайте клавишу (ОК) для вызова контекстного меню', helper_cleared: 'Успешно, подсказки будут показаны заново.', helper_torrents_view: 'Для сброса тайм-кода и вызова меню удерживайте клавишу (ОК)', fav_sync_title: 'Синхронизация закладок', fav_sync_text: 'Ваши любимые закладки вместе с Вами. Подключите синхронизацию и просматривайте на любом устройстве.

    Для этого зарегистрируйтесь на сайте {site}, создайте профиль и авторизуйтесь в приложение.', fav_sync_site: 'Сайт', fav_remove_title: 'Удалить из истории', fav_remove_descr: 'Удалить выделенную карточку', fav_clear_title: 'Очистить историю', fav_clear_descr: 'Удалить все карточки из истории', fav_clear_label_title: 'Очистить метки', fav_clear_label_descr: 'Очистить метки о просмотрах', fav_clear_time_title: 'Очистить тайм-коды', fav_clear_time_descr: 'Очистить все тайм-коды', fav_label_cleared: 'Отметки очищены', fav_time_cleared: 'Тайм-коды очищены', timetable_empty: 'В этом разделе будут отображаться даты выхода новых серий', player_quality: 'Качество', player_flow: 'Поток', player_tracks: 'Аудиодорожки', player_disabled: 'Отключено', player_unknown: 'Неизвестно', player_subs: 'Субтитры', player_size_default_title: 'По умолчанию', player_size_default_descr: 'Размер видео по умолчанию', player_size_cover_title: 'Расширить', player_size_cover_descr: 'Расширяет видео на весь экран', player_size_fill_title: 'Заполнить', player_size_fill_descr: 'Вместить видео на весь экран', player_size_s115_title: 'Увеличить 115%', player_size_s115_descr: 'Увеличить видео на 115%', player_size_s130_title: 'Увеличить 130%', player_size_s130_descr: 'Увеличить видео на 130%', player_size_v115_title: 'По вертикали 115%', player_size_v115_descr: 'Увеличить видео на 115%', player_size_v130_title: 'По вертикали 130%', player_size_v130_descr: 'Увеличить видео на 130%', player_video_size: 'Размер видео', player_playlist: 'Плейлист', player_error_one: 'Не удалось декодировать видео', player_error_two: 'Видео не найдено или повреждено', player_start_from: 'Продолжить просмотр с', player_not_found: 'Плеер не найден', player_lauch: 'Запустить плеер', player_speed_default_title: 'Обычная', player_speed_two_descr: 'Воспроизводиться без звука', player_video_speed: 'Скорость воспроизведения', player_share_title: 'Поделиться', player_share_descr: 'Запустить это видео на другом устройстве', player_normalization_power_title: 'Сила нормализации', player_normalization_smooth_title: 'Скорость нормализации', player_normalization_step_none: 'Нулевая', player_normalization_step_low: 'Низкое', player_normalization_step_medium: 'Среднее', player_normalization_step_hight: 'Высокое', player_normalization: 'Нормализация', player_normalization_type_title: 'Тип нормализации', player_normalization_type_all: 'Все', player_normalization_type_up: 'Только повышение', player_normalization_type_down: 'Только понижение', player_youtube_no_played: 'К сожалению, это видео не доступно в вашем регионе, возможно, оно было заблокировано или удалено.', player_youtube_start_play: 'Для начала проигрывания видео, нажмите кнопку "Плей"', player_youtube_no_support: 'Ваше устройство не поддерживает воспроизведение YouTube видео, проверьте интернет соединение.', player_segments_title: 'Сегменты', player_segments_descr: 'Пропуск интро и другие части видео', player_segments_ad_title: 'Рекламные вставки', player_segments_skip_title: 'Интро и другое', player_segments_value_none: 'Не пропускать', player_segments_value_auto: 'Автопропуск', player_segments_value_user: 'Пользовательский', player_segments_skiped: 'Сегмент пропущен', player_segments_skip_now: 'Пропустить', player_segments_skip_in: 'Пропуск через', player_segments_skip_intro: 'Пропустить заставку', player_segments_next: 'Следующая серия', player_segments_next_in: 'Следующая серия через', player_pip: 'Уменьшить', player_volume: 'Громкость', player_time_end: 'Конец в', broadcast_open: 'Открыть карточку на другом устройстве', broadcast_play: 'Выберите устройство для просмотра', card_new_episode: 'Новая серия', card_book_remove: 'Убрать из закладок', card_book_add: 'В закладки', card_book_descr: 'Смотрите в меню (Закладки)', card_like_remove: 'Убрать из понравившихся', card_like_add: 'Нравится', card_like_descr: 'Смотрите в меню (Нравится)', card_wath_remove: 'Убрать из ожидаемых', card_wath_add: 'Смотреть позже', card_wath_descr: 'Смотрите в меню (Позже)', card_history_remove: 'Убрать из истории', card_history_add: 'В историю', card_history_descr: 'Смотрите в меню (История)', keyboard_listen: 'Говорите, я слушаю...', keyboard_nomic: 'Нет доступа к микрофону', notice_new_quality: 'Доступно новое качество', notice_quality: 'Качество', notice_new_episode: 'Новая серия', notice_none: 'У вас еще нет никаких уведомлений, зарегистрируйтесь на сайте {site}, чтобы следить за новыми сериями и релизами.', notice_in_quality: 'В качестве', notice_none_account: 'У вас еще нет никаких уведомлений, добавьте сериалы в закладки и ожидайте уведомления о новых сериях.', notice_none_system: 'На данный момент у вас отсутствуют уведомления. Мы обязательно оповестим вас, когда появятся новые уведомления.', copy_link: 'Копировать ссылку на видео', copy_secuses: 'Ссылка скопирована в буфер обмена', copy_error: 'Ошибка при копирование ссылки', copy_link_buffer: 'Скопировать ссылку', account_sync_to_profile: 'Все закладки будут перенесены в профиль', account_sync_secuses: 'Все закладки успешно перенесены', account_profiles: 'Профили', account_profiles_empty: 'Не удалось получить список профилей', account_authorized: 'Авторизованы', account_logged_in: 'Вы вошли под аккаунтом', account_login_failed: 'Вход не выполнен', account_login_wait: 'Ожидаем входа в аккаунт', account_profile_main: 'Общий', account_profile_child: 'Детский', account_profile_add: 'Добавить профиль', account_profile_name: 'Имя профиля', account_profile_created: 'Профиль успешно создан', account_profile_limited: 'Достигнут максимальный лимит профилей: {count}', account_profile_info: 'Вы можете управлять профилями в настройках аккаунта, для этого перейдите на сайт ', account_export_secuses: 'Экспорт успешно завершён', account_export_fail: 'Ошибка при экспорте', account_export_fail_500: 'Неизвестная ошибка', account_export_fail_610: 'Вы исчерпали лимит на сегодня', account_export_fail_200: 'Ошибка базы данных, попробуйте позже', account_export_fail_600: 'Ошибка, не удалось загрузить файл', account_export_fail_620: 'Ошибка, файл не передан', account_import_secuses: 'Импорт успешно завершён', account_import_fail: 'Ошибка при импорте', account_imported: 'импортировано', account_reload_after: 'перезагрузка через 5 сек.', account_create: 'Откройте больше возможностей с аккаунтом CUB. Зарегистрируйтесь на сайте и получите доступ к синхронизации ваших закладок, тайм-кодов и других возможностей аккаунта CUB.', account_premium: 'Раскройте новые горизонты с аккаунтом CUB Premium! Наслаждайтесь увеличенными лимитами и обогащенным функционалом сервиса. Перейдите на сайт и узнайте больше о преимуществах премиум аккаунта.', account_premium_more: 'Подробнее о CUB Premium', account_limited: 'Вы достигли максимального лимита. Увеличьте лимит с аккаунтом CUB Premium. Подробнее на сайте ', account_premium_include_1: 'Увеличение количества закладок', account_premium_include_2: 'Увеличение истории просмотров', account_premium_include_3: 'Увеличение количества тайм-кодов', account_premium_include_4: 'Количество профилей на аккаунт', account_premium_include_5: 'Уведомления', account_premium_include_6: 'Синхронизация данных', account_premium_include_text_1: 'Больше закладок - больше возможностей! Сохраняйте свои любимые фильмы и сериалы, создавайте списки просмотра и наслаждайтесь просмотром в любое удобное время.', account_premium_include_text_2: 'Увеличьте историю просмотров в приложении и следите за тем, что уже посмотрели. Легко находите и пересматривайте свои любимые фильмы и сериалы.', account_premium_include_text_3: 'Не бойтесь пропустить ни одной важной сцены! Увеличьте количество тайм-кодов в приложении и легко отслеживайте, где остановились в просмотре любимых фильмов и сериалов.', account_premium_include_text_4: 'Получите больше свободы с нашим премиум доступом! Увеличьте количество профилей на аккаунте и позвольте своим друзьям и близким наслаждаться фильмами и сериалами вместе с вами. Никаких ограничений - наслаждайтесь просмотром с любимыми людьми.', account_premium_include_text_5: 'Не пропустите ни одной новой серии или перевода! Получайте уведомления вовремя и будьте в курсе всех обновлений. Увеличьте свой кинопоток вместе с нами и получайте уведомления о выходе новых серий и переводов прямо на свой смартфон.', account_premium_include_text_6: 'Синхронизуйте свои данные между устройствами с премиум доступом! Больше не нужно тратить время на поиск последнего эпизода, на котором вы остановились. С нашим премиум доступом вы можете синхронизировать свои данные между устройствами, чтобы продолжать просмотр с места, где вы остановились, на любом устройстве, где установлено приложение.', account_code_enter: 'Введите шестизначный код', account_code_error: 'Возможно, вы ввели неверный или устаревший код', account_code_wrong: 'Возможно, вы указали неверный формат', account_code_where: 'Перейдите на сайт и введите указанный там код.', account_code_input: 'Ввести код', account_qr_premium: 'Отсканируйте QR-код, чтобы узнать больше о CUB Premium', account_qr_code: 'Отсканируйте QR-код для получения кода добавления устройства', account_qr_create: 'Отсканируйте QR-код, чтобы создать аккаунт', account_add_device_title: 'Добавить устройство', account_add_device_text: 'Перейдите на сайт по ссылки чтобы получить код для добавления устройства.', account_none_title: 'Еще нет аккаунта?', account_none_include_1: 'Закладки и история просмотров сохраняются в облаке', account_none_include_2: 'Синхронизация между устройствами', account_none_include_3: 'Уведомления о новых сериях и релизах', account_none_include_4: 'Создание нескольких профилей на одном аккаунте', account_discuss_add_qr: 'Отсканируйте QR-код', account_discuss_add_title: 'Хотите оставить комментарий?', account_discuss_add_text: 'Отсканируйте QR-код, чтобы перейти в приложение и добавить комментарий к этому фильму или сериалу.

    - Возьмите телефон или планшет
    - Откройте камеру
    - Наведите на QR-код
    - Перейдите по ссылки, которая появится на экране', account_discuss_added: 'Спасибо за ваш комментарий!', account_discuss_added_ready: 'Вы уже оставили комментарий к этому фильму или сериалу.', network_noconnect: 'Нет подключения к сети', network_404: 'Запрошенная страница не найдена. [404]', network_401: 'Авторизация не удалась', network_500: 'Внутренняя ошибка сервера. [500]', network_parsererror: 'Запрошенный синтаксический анализ JSON завершился неудачно.', network_timeout: 'Время запроса истекло.', network_abort: 'Запрос был прерван.', network_error: 'Неизвестная ошибка', size_zero: '0 Байт', size_byte: 'Байт', size_kb: 'КБ', size_mb: 'МБ', size_gb: 'ГБ', size_tb: 'ТБ', size_pp: 'ПБ', speed_bit: 'бит/c', speed_kb: 'Кбит/c', speed_mb: 'Мбит/c', speed_gb: 'Гбит/c', speed_tb: 'Тбит/c', speed_pp: 'Пбит/c', month_1: 'Январь', month_2: 'Февраль', month_3: 'Март', month_4: 'Апрель', month_5: 'Ма', month_6: 'Июнь', month_7: 'Июль', month_8: 'Август', month_9: 'Сентябрь', month_10: 'Октябрь', month_11: 'Ноябрь', month_12: 'Декабрь', day_1: 'Понедельник', day_2: 'Вторник', day_3: 'Среда', day_4: 'Четверг', day_5: 'Пятница', day_6: 'Суббота', day_7: 'Воскресенье', month_1_e: 'Января', month_2_e: 'Февраля', month_3_e: 'Марта', month_4_e: 'Апреля', month_5_e: 'Мая', month_6_e: 'Июня', month_7_e: 'Июля', month_8_e: 'Августа', month_9_e: 'Сентября', month_10_e: 'Октября', month_11_e: 'Ноября', month_12_e: 'Декабря', week_1: 'Пн', week_2: 'Вт', week_3: 'Ср', week_4: 'Чт', week_5: 'Пт', week_6: 'Сб', week_7: 'Вс', time_h: 'ч.', time_m: 'м.', time_s: 'с.', extensions_enable: 'Включить', extensions_disable: 'Отключить', extensions_check: 'Проверить статус', extensions_install: 'Установить', extensions_info: 'Информация', extensions_edit: 'Редактировать', extensions_change_name: 'Изменить название', extensions_change_link: 'Изменить ссылку', extensions_remove: 'Удалить', extensions_set_name: 'Введите название плагина', extensions_set_url: 'Введите адрес плагина', extensions_ready: 'Этот плагин уже установлен', extensions_no_info: 'Без информации', extensions_no_name: 'Без названия', extensions_worked: 'Рабочий', extensions_no_plugin: 'Плагин не подтверждён ', extensions_add: 'Добавить плагин', extensions_from_memory: 'Установленные в память ', extensions_from_cub: 'Установленные из CUB', extensions_from_popular: 'Популярные плагины', extensions_from_lib: 'Библиотека плагинов', extensions_from_connected: 'Подключенные плагины', extensions_hpu_best: 'Популярные', extensions_hpu_recomend: 'Рекомендуем', extensions_hpu_theme: 'Темы', extensions_hpu_screensaver: 'Скринсейвер', extensions_hpu_video: 'Видео', extensions_hpu_control: 'Управление', extensions_hpu_other: 'Разное', extensions_hpu_: 'Остальное', speedtest_connect: 'подключение', speedtest_test: 'тестирование', speedtest_ready: 'готово', speedtest_button: 'Тестировать скорость', change_source_on_cub: 'Сменить источник на CUB', tv_status_returning_series: 'Онгоинг', tv_status_planned: 'Запланировано', tv_status_in_production: 'В производстве', tv_status_ended: 'Завершено', tv_status_canceled: 'Отменено', tv_status_pilot: 'Пилотный', tv_status_released: 'Выпущенный', tv_status_rumored: 'По слухам', tv_status_post_production: 'Скоро', input_detection_touch: 'Хотите переключить на сенсорное управление?', input_detection_mouse: 'Хотите переключить на управление мышью?', input_detection_remote: 'Хотите переключить на управление пультом?', https_text: 'Вы используете протокол HTTPS, с которым Lampa работает некорректно. Для корректной работы приложения используйте адрес с протоколом HTTP', premiere_author_recomend_1: 'Закажите пиццу и готовьтесь к драйву.', premiere_author_recomend_2: 'Вечеринка только начинается.', premiere_author_recomend_3: 'Подготовьтесь к адреналину.', premiere_author_recomend_4: 'Готовы к незабываемому вечеру?', premiere_author_recomend_5: 'Рекомендую к просмотру.', premiere_title: 'Премьера', termsofuse_t_01: 'Правила использования', termsofuse_t_02: 'Lampa - это удобный инструмент для ознакомления с новинками киноиндустрии, включая фильмы, сериалы, мультфильмы и другую информацию. Для получения информации о фильмах используется открытый источник - сайт themoviedb.org', termsofuse_t_03: 'Контент', termsofuse_t_04: 'Lampa использует API (Application Programming Interface) от каталога themoviedb.org для получения информации о фильмах и телесериалах, а также для просмотра связанных с ними медиафайлов, таких как постеры, трейлеры и другое.', termsofuse_t_05: 'API от themoviedb.org позволяет приложению Lampa получать доступ к базе данных фильмов и телесериалов, предоставляя возможность поиска медиафайлов и просмотра информации о них.', termsofuse_t_06: 'Взаимодействие между приложением Lampa и themoviedb.org происходит через запросы к API, которые отправляются из приложения Lampa на серверы themoviedb.org для получения информации и медиафайлов. Themoviedb.org обрабатывает запросы и отправляет обратно в приложение Lampa запрошенные данные.', termsofuse_t_07: 'Таким образом, благодаря использованию API от themoviedb.org, приложение Lampa предоставляет пользователям доступ к обширной базе данных фильмов и телесериалов, соблюдая при этом условия использования и ограничения, установленные правообладателем.', termsofuse_t_08: 'Расширения', termsofuse_t_09: 'Расширения - это мощный инструмент, предоставляемый для приложения Lampa, который позволяет пользователям расширить его возможности, добавить дополнительный функционал и изменить контент в соответствии с их потребностями.', termsofuse_t_10: 'Однако, следует отметить, что пользователи устанавливают расширения на свой страх и риск. Все последствия, возникающие в результате использования расширений, ложатся на пользователя. Пользователям рекомендуется быть осторожными при установке и использовании расширений, тщательно оценивая их надежность и безопасность.', termsofuse_t_11: 'Нарушения авторских прав', termsofuse_t_12: 'Если вы обнаружили нарушение авторских прав в приложении Lampa - свяжитесь с разработчиками, используя раздел Контакты приложения. Однако, важно учесть, что приложение Lampa использует API от каталога themoviedb.org, который предоставляет открытый доступ к информации о фильмах и сериалах. Это означает, что информация, которая отображается в приложении Lampa, является общедоступной и не нарушает авторских прав.', termsofuse_t_13: 'Если вы считаете, что в приложении Lampa все же есть контент, нарушающий авторские права - обратитесь к правообладателю этого контента с просьбой удалить его со страниц сайта themoviedb.org. В свою очередь, приложение Lampa может удалить контент при получении соответствующего уведомления о нарушении авторских прав.', install_app_apk_title: 'Как установить Lampa на Android', install_app_apk_text: 'Мы заметили, что для запуска Lampa на Android вы используете MediaStationX. Мы рекомендуем вам установить Lampa в виде APK-приложения. Это обеспечит более стабильную работу и доступ ко всем функциям приложения.

    Перейдите в группу в Телеграме, чтобы скачать последнюю версию APK и инструкции по установке.', install_app_apk_qr: 'Отсканируйте QR-код, чтобы скачать Lampa APK', ad: 'Реклама', ad_disable: 'Подписавшись на CUB Premium, вы забудете о рекламе.', ad_plugin: 'Реклама от плагина', ad_after: 'Реклама через', ad_skip: 'Пропустить', ad_continue_after: 'Просмотр начнется через', discuss_rules_title: 'Следуйте правилам и уважайте других — создаём комфорт вместе!', discuss_rules_rule_1: 'Не используйте слова, состоящие только из цифр.', discuss_rules_rule_2: 'Пишите на одном языке, используйте слова из 5 и более букв.', discuss_rules_rule_3: 'Используйте знаки препинания и завершайте предложения точками.', discuss_rules_rule_4: 'Избегайте непонятных символов !?#%', discuss_rules_rule_5: 'Пишите полные предложения, используйте предложение из 5 и более слов.', discuss_rules_rule_6: 'Не используйте слишком длинные слова.', discuss_rules_rule_7: 'Постарайтесь уместиться в 300 символов.', discuss_voited: 'Спасибо за вашу оценку! Ваш голос помогает улучшать контент.', confirm_open_player: 'Другое устройство запрашивает доступ к плееру. Хотите воспроизвести видео?', pincode_use_remote: 'Используйте пульт для ввода кода, нажимая стрелки для выбора нужной цифры.', pincode_use_toggle: 'Нажмите и удерживайте кнопку OK, чтобы сменить раскладку.', storage_quota_exceeded: 'Превышен лимит хранилища. Удалите кеш и повторите попытку.', ai_search_start_typing: 'Спросите у AI-ассистента, какие фильмы вы хотите найти. Например: "Фильмы про космос"', ai_search_limit: 'Лимит запросов превышен. Попробуйте повторить запрос позже. С подпиской CUB Premium лимиты будут увеличены, и вам не придется ждать!', ai_subscribe_title: 'Откройте больше возможностей с AI', ai_subscribe_descr: 'Оформите подписку CUB Premium и увеличьте количество запросов к AI. Вместе с AI мы подберем вам лучшие фильмы которые только сможем.', dmca_title: 'Контент заблокирован', dmca_descr: 'Данный материал недоступен для просмотра. Он был заблокирован по просьбе правообладателя в соответствии с законодательством об авторских правах.', dmca_title_lgbt: 'Контент ограничен', dmca_descr_lgbt: 'Просмотр недоступен в вашем регионе, так как возможно содержит ЛГБТ-контент. В некоторых странах такие материалы могут быть запрещены или ограничены.', loading_progress_step_0: 'Подготовка', loading_progress_step_1: 'Загрузка языков', loading_progress_step_2: 'Поиск зеркал', loading_progress_step_3: 'Подготовка расширений', loading_progress_step_4: 'Осталось немного', loading_progress_step_5: 'Уже скоро', install_extension_rule_1: 'Установка стороннего плагина', install_extension_rule_2: 'Плагины создаются независимыми разработчиками, и LAMPA не проверяет их содержимое. Устанавливая их, вы соглашаетесь с тем, что:', install_extension_rule_3: '- Некоторые плагины могут запрашивать доступ к вашим данным или работать с непроверенными источниками.', install_extension_rule_4: '- Если устанавливаемый вами плагин нарушает авторские права или другие законы, вся ответственность за его использование лежит на вас.', install_extension_rule_5: '- Устанавливайте только плагины из доверенных источников. Если вы сомневаетесь в безопасности или законности плагина, лучше откажитесь от его установки.', install_extension_rule_6: 'LAMPA не поддерживает и не одобряет плагины, связанные с пиратским контентом.', install_extension_rule_7: 'Продолжая, вы подтверждаете, что понимаете эти риски.', terminal_title: 'Терминал', terminal_no_access: 'Без доступа', terminal_confirm: 'Разрешить', terminal_deny: 'Запретить', terminal_update: 'Обновить', terminal_code: 'Код доступа', terminal_text: 'Вы запрашиваете доступ к терминалу. Разработчики могут получить доступ к вашему устройству и управлять им. Это может быть полезно для диагностики и устранения проблем, но также может представлять риск безопасности. Пожалуйста, убедитесь, что вы доверяете разработчикам, прежде чем предоставлять доступ.

    Вы можете в любой момент отключить эту функцию нажав на кнопку "Запретить".', developer_param_enabled: 'Режим разработчика', developer_param_nopremium: 'Отключить CUB Premium', developer_param_nodemo: 'Отключить демонстрационный режим', developer_param_ads: 'Показывать рекламу', developer_param_fps: 'Показывать FPS', developer_trigger_help_1: 'Почти догадались :)', developer_trigger_help_2: 'Еще немного :)', developer_trigger_help_3: 'Точно хочешь войти в режим разработчика?', developer_trigger_help_4: 'Режим разработчика активирован', remote_helper_long: 'Удерживайте кнопку (ОК) для вызова меню', no_watch_history: 'Нет истории просмотра', adult_content_title: 'Взрослый контент', adult_content_text_warning: 'Контент может содержать материалы для взрослых, которые могут быть неуместны для некоторых зрителей.', adult_content_text_modal: 'Этот фильм или сериал помечен как содержащий взрослый контент. Для просмотра этого материала вам должно быть 18 лет или больше. Пожалуйста, подтвердите, что вы достигли совершеннолетия, чтобы продолжить просмотр.', adult_content_confirm: 'Мне 18 лет или больше', adult_content_deny: 'Мне меньше 18 лет', inner_player_disclaimer_title: 'Дисклеймер плеера', inner_player_disclaimer_text: 'Я понимаю, что запускаю плеер с неполной поддержкой, и понимаю, что это может приводить к нетипичному поведению устройства. Оптимально перейти на плееры семейства tvOS или другие альтернативы.', inner_player_disclaimer_change_player: 'Сменить плеер', remote_configuration_settings_title: 'Удалённая конфигурация', remote_configuration_registration_text: 'Удалённая конфигурация предоставляет временный доступ к вашему приложению, другое приложение сможет установить расширения и настроить параметры.', remote_configuration_waite_text: 'Теперь вы можете передать этот код в другое приложение, чтобы предоставить доступ к настройкам.', remote_configuration_open_button: 'Открыть доступ', remote_configuration_code_life: 'Код действителен', remote_configuration_notice_nocode: 'Не удалось открыть доступ', player_ai_agent_info: 'Хотите узнать больше о видео? Спросите у AI-ассистента, и он предоставит вам график настроений, краткий сюжет и интересные моменты.', player_ai_agent_ask_plot: 'Кратко о сюжете', player_ai_agent_ask_moods: 'Показать график', player_ai_agent_ask_highlights: 'Интересные моменты', player_ai_agent_ask: 'Спросить', player_ai_agent_processing: 'Анализирую...', player_ai_agent_no_data: 'Нет данных для показа.', player_ai_agent_no_analysis: 'Не удалось провести анализ видео.', player_ai_agent_quota: 'Лимит запросов превышен. Попробуйте повторить запрос позже.', player_ai_agent_working: 'Я еще работаю над этим...' }; var en = { lang_choice_title: 'Welcome', lang_choice_subtitle: 'Choose your language', more: 'More', show_more: 'Show more', more_results: 'Show more results', loading: 'Loading', nofind_movie: 'The movie could not be found.', noname: 'Untitled', nochoice: 'Not chosen', cancel: 'Cancel', confirm: 'I confirm', sure: 'Are you sure?', nodata: 'No data', back: 'Back', ready: 'Ready', close_app_modal: 'Are you sure you want to leave the app?', connected_seeds: 'connected', search: 'Search', search_input: 'Enter text', search_empty: 'Search history is empty.', search_delete: 'Left - delete', search_start_typing: 'Start typing search text.', search_searching: 'Search in progress...', search_start: 'To start searching', search_nofound: 'Nothing was found according to your request.', global_search: 'Global search', full_genre: 'Genre', full_production: 'Production', full_date_of_release: 'date of release', full_budget: 'Budget', full_countries: 'Countries', full_like: 'Like', full_torrents: 'Torrents', full_trailers: 'Trailers', full_detail: 'In detail', full_notext: 'No description.', full_series_release: 'Series release', full_next_episode: 'Next', full_episode_days_left: 'Days left', full_trailer_official: 'Official', full_trailer_no_official: 'Informal', full_season: 'Season', full_episode: 'Episode', full_directing: 'Directing', full_writing: 'Writing', full_keywords: 'Tags', settings_cub_sync: 'Synchronization', settings_cub_sync_descr: 'Synchronization with the CUB service: Synchronization of your bookmarks, browsing history, tags and timecodes.', settings_cub_account: 'Account', settings_cub_logged_in_as: 'Logged in as', settings_cub_profile: 'Profile', settings_cub_sync_btn: 'Synchronize', settings_cub_sync_btn_descr: 'Save local bookmarks to CUB account', settings_cub_backup: 'Backup', settings_cub_backup_descr: 'Save or load backup data', settings_cub_logout: 'Sign out', settings_cub_signin: 'Authorization', settings_cub_not_specified: 'Not specified', settings_cub_password: 'Password', settings_cub_status: 'Status', settings_cub_backup_import: 'Import', settings_cub_backup_export: 'Export', settings_cub_sync_filters: 'Filters sync', settings_cub_sync_calendar: 'Calendar sync', settings_cub_sync_quality: 'Quality sync', settings_cub_sync_search: 'Search history sync', settings_cub_sync_recomends: 'Recommendations sync', settings_cub_sync_timecodes: 'Timecode Synchronization', settings_input_links: 'Favorites', settings_interface_type: 'Lite version', settings_interface_size: 'Interface size', settings_interface_menu_always: 'Always show the left menu', settings_interface_background: 'Background', settings_interface_background_use: 'Show background', settings_interface_background_type: 'Background type', settings_interface_performance: 'Performance', settings_interface_animation: 'Animation', settings_interface_animation_descr: 'Animation of cards and content', settings_interface_attenuation: 'Attenuation', settings_interface_attenuation_descr: 'Smooth fading of cards from below and from above', settings_interface_scroll: 'Scroll Type', settings_interface_view_card: 'Card view type', settings_interface_view_card_descr: 'As you scroll the feed, the cards will load gradually or load all', settings_interface_lang: 'Interface language', settings_interface_lang_reload: 'You need to restart the application, click "OK" to restart.', settings_main_account: 'Account', settings_main_interface: 'Interface', settings_main_player: 'Player', settings_main_parser: 'Parser', settings_main_torrserver: 'TorrServer', settings_main_plugins: 'Extensions', settings_main_rest: 'Other', settings_rest_start: 'Start page', settings_rest_start_descr: 'Which page to start at startup', settings_rest_source: 'Source', settings_rest_source_use: 'Main source', settings_rest_source_descr: 'Where to get information about films', settings_rest_tmdb_lang: 'What language to display data from TMDB', settings_rest_tmdb_prox: 'Proxy TMDB', settings_rest_tmdb_prox_auto: 'Enable proxy automatically', settings_rest_tmdb_posters: 'Resolution of TMDB posters', settings_rest_screensaver: 'Screensaver', settings_rest_screensaver_use: 'Show splash screen when idle', settings_rest_screensaver_type: 'Screen saver type', settings_rest_helper: 'Hints', settings_rest_helper_use: 'Show hints', settings_rest_helper_reset: 'Show hints again', settings_rest_pages: 'How many pages to keep in memory', settings_rest_pages_descr: 'Keeps pages in the state you left them in', settings_rest_time: 'Shift time', settings_rest_navigation: 'Navigation type', settings_rest_keyboard: 'Keyboard type', settings_rest_device: 'Device name', settings_rest_device_placeholder: 'For example: My Lamp', settings_rest_cache: 'Clear cache', settings_rest_cache_descr: 'All settings and data will be cleared', settings_rest_tmdb_example: 'For example:', settings_rest_tmdb_api_descr: 'To get data', settings_rest_tmdb_image_descr: 'To get images', settings_rest_card_quality: 'Quality marks', settings_rest_card_quality_descr: 'Display quality marks on cards', settings_rest_card_episodes: 'Episode marks', settings_rest_card_episodes_descr: 'Display episode markers on cards', settings_parser_use: 'Use parser', settings_parser_use_descr: 'Hereby, you agree to accept all responsibility for the use of public links to view torrent and online content.', settings_parser_type: 'Parser type for torrents', settings_parser_jackett_placeholder: 'For example: 192.168.x', settings_parser_jackett_link: 'Link', settings_parser_jackett_link_descr: 'Provide a link to the Jackett script', settings_parser_jackett_key_placeholder: 'For example: sa0sk83d..', settings_parser_jackett_key: 'Api key', settings_parser_jackett_key_descr: 'Located in Jackett', settings_parser_torlook_type: 'TorLook site parsing method', settings_parser_scraperapi_placeholder: 'For example: scraperapi.com', settings_parser_scraperapi_link: 'Link to site parser', settings_parser_scraperapi_descr: 'Register on the site scraperapi.com, enter the link api.scraperapi.com?api_key=...&url={q}
    W41.torlook.info will be delivered to {q}', settings_parser_search: 'Search', settings_parser_search_descr: 'What language to search in?', settings_parser_in_search: 'Parser in search', settings_parser_in_search_descr: 'Show search results?', settings_parser_timeout_title: 'Parser timeout', settings_parser_timeout_descr: 'Time in seconds to wait for a response from the server', settings_player_type: 'Player type', settings_player_type_descr: 'Which player to play', settings_infuse_launch_mode: 'Launch in Infuse', settings_infuse_launch_mode_descr: 'What to do when opening video in Infuse. Torrents always play immediately', settings_infuse_launch_ask: 'Ask on launch', settings_infuse_launch_play: 'Play', settings_infuse_launch_save_and_play: 'Save and play', settings_player_iptv_type: 'Player type for IPTV', settings_player_iptv_type_descr: 'Which player to play IPTV channels', settings_player_reset: 'Reset default player', settings_player_reset_descr: 'Resets the selected Android player in the application', settings_player_path: 'Path to the player', settings_player_path_descr: 'Specify the path to the player .exe', settings_player_external_header: 'External player', settings_player_external_fullscreen: 'Launch in fullscreen mode', settings_player_normalization: 'Sound normalization', settings_player_normalization_descr: 'Normalizes sound to one level, lowers loud sounds and boosts quiet ones.', settings_player_next_episode: 'Next episode', settings_player_next_episode_descr: 'Automatically switch to the next series after the end of the current one', settings_player_timecode: 'Timecode', settings_player_timecode_descr: 'Continue from last viewed location', settings_player_scale: 'Scaling Method', settings_player_scale_descr: 'How to calculate video scaling', settings_player_subs: 'Subtitles', settings_player_subs_use: 'Turn on', settings_player_subs_use_descr: 'Always turn on subtitles after starting a video', settings_player_subs_lang: 'Subtitle language', settings_player_subs_lang_descr: 'Which subtitles to turn on automatically when several languages are available', settings_param_subtitles_lang_auto: 'Interface language', settings_player_subs_size: 'The size', settings_player_subs_size_descr: 'Screen size of subtitles', settings_player_subs_stroke_use: 'Use edging', settings_player_subs_stroke_use_descr: 'Subtitles will be outlined in black for better readability', settings_player_subs_backdrop_use: 'Use an underlay', settings_player_subs_backdrop_use_descr: 'Subtitles will be displayed on a translucent backing to improve readability', settings_player_quality: 'Default video quality', settings_player_quality_descr: 'Preferred video quality for viewing', settings_player_hls_title: 'Processing the .m3u8 stream', settings_player_hls_descr: 'Do not touch this parameter if you do not know why it is.', settings_plugins_notice: 'To apply the plugin, you need to restart the application', settings_plugins_add: 'Add Plugin', settings_plugins_add_descr: 'To delete an added plugin, hold or double-click the (OK) key on it', settings_plugins_install: 'Install Plugin', settings_plugins_install_descr: 'Install a plugin from the list of available', settings_server_link: 'Use link', settings_server_links: 'Links', settings_server_placeholder: 'For example: 192.168.X', settings_server_link_one: 'Main Link', settings_server_link_one_descr: 'Specify the main link to the TorrServer script', settings_server_link_two: 'Additional link', settings_server_link_two_descr: 'Provide an additional link to the TorrServer script', settings_server_additionally: 'Additionally', settings_server_client: 'Embedded client', settings_server_client_descr: 'Use the built-in TorrServer JS client, otherwise the system one starts.', settings_server_base: 'Save to database', settings_server_base_descr: 'The torrent will be added to the TorrServer database', settings_server_preload: 'Use prefetch buffer', settings_server_preload_descr: 'Wait for TorrServer\'s preload buffer to fill before playing', settings_server_auth: 'Authorization', settings_server_password_use: 'Password login', settings_server_login: 'Login', settings_server_password: 'Password', settings_server_not_specified: 'Not specified', settings_server_search_type: 'Search type', settings_server_search_type_descr: 'Where to look for torrents: built-in rutor, Torznab indexers in TorrServer, or both sources.', settings_param_torrserver_search_rutor: 'Rutor', settings_param_torrserver_search_torznab: 'Torznab', settings_param_torrserver_search_both: 'Rutor + Torznab', torent_nohash_reasons: 'The reasons', torent_nohash_reason_one: 'TorServer was unable to download the torrent file', torent_nohash_reason_two: 'Reply from TorServer', torent_nohash_reason_three: 'Link', torent_nohash_do: 'What to do?', torent_nohash_do_one: 'Check if you configured Jackett correctly', torent_nohash_do_two: 'Private sources may not provide a link to the file', torent_nohash_do_three: 'Make sure Jackett can download the file too', torent_nohash_do_four: 'Write to our telegram group: @lampa_group', torent_nohash_do_five: 'Specify which movie, which distribution and, if possible, a photo of this distribution', torrent_error_text: 'Failed to connect to TorrServer. Let\'s quickly go through the list of possible problems and check everything.', torrent_error_step_1: 'Is TorrServer running', torrent_error_step_2: 'Dynamic IP', torrent_error_step_3: 'Protocol and Port', torrent_error_step_4: 'Antivirus blocking', torrent_error_step_5: 'Check for availability', torrent_error_step_6: 'Still doesn\'t work', torrent_error_info_1: 'Make sure you have launched TorrServer on the device where it is installed.', torrent_error_info_2: 'A common mistake, the IP address of the device with TorrServer has changed. Make sure that the IP address you entered - {ip} - matches the address of the device on which TorrServer is installed.', torrent_error_info_3: 'To connect to TorrServer, you must specify the protocol http:// at the beginning and port :8090 at the end of the address. Make sure there is a port after the IP address, your current address is {ip}', torrent_error_info_4: 'Frequent occurrence, antivirus or firewall can block access by IP address, try disabling antivirus and firewall.', torrent_error_info_5: 'On any other device on the same network, open the {ip} address in a browser and check if the TorrServer web interface is available.', torrent_error_info_6: 'If, after all the checks, a connection error still occurs, try restarting TorrServer and the Internet adapter.', torrent_error_info_7: 'If the problem persists, write to the Telegram group @lampa_group with the text (Lampa does not connect to TorrServer after all checks, the current address is {ip})', torrent_error_start: 'Start verification', torrent_error_nomatrix: 'Failed to verify Matrix version', torrent_error_made: 'Performed', torrent_error_from: 'from', torrent_error_next: 'Further', torrent_error_complite: 'To complete', torrent_error_connect: 'Connection error', torrent_install_need: 'Requires TorrServer', torrent_install_text: 'TorrServer is an application that allows you to view content from torrent files online.

    More detailed information on installation can be found in the Telegram groups below.', torrent_install_contact: 'Telegram groups', torrent_item_bitrate: 'Bitrate', torrent_item_seeds: 'Seeds', torrent_item_grabs: 'Leechers', torrent_item_mb: 'Mbps', torrent_serial_episode: 'Series', torrent_serial_season: 'Season', torrent_serial_date: 'Exit', torrent_get_magnet: 'Requesting a magnet link', torrent_remove_title: 'Delete', torrent_remove_descr: 'The torrent will be removed from your list', torrent_parser_any_one: 'Any', torrent_parser_any_two: 'Any', torrent_parser_no_choice: 'Not chosen', torrent_parser_yes: 'Yes', torrent_parser_no: 'No', torrent_parser_quality: 'Quality', torrent_parser_subs: 'Subtitles', torrent_parser_voice: 'Translation', torrent_parser_tracker: 'tracker', torrent_parser_year: 'Year', torrent_parser_season: 'Season', torrent_parser_sort_by_seeders: 'By distributors', torrent_parser_sort_by_size: 'To size', torrent_parser_sort_by_name: 'by name', torrent_parser_sort_by_tracker: 'By source', torrent_parser_sort_by_date: 'By date', torrent_parser_sort_by_viewed: 'Viewed', torrent_parser_voice_dubbing: 'Dubbing', torrent_parser_voice_polyphonic: 'Polyphonic', torrent_parser_voice_two: 'Two-voiced', torrent_parser_voice_amateur: 'Amateur', torrent_parser_reset: 'Reset filter', torrent_parser_empty: 'Failed to get results', torrent_parser_no_hash: 'Failed to get HASH, try reloading TorrServer', torrent_parser_added_to_mytorrents: 'added to "My torrents"', torrent_parser_add_to_mytorrents: 'Add to "My torrents"', torrent_parser_label_title: 'Flag', torrent_parser_label_descr: 'Flag a hand with a flag (viewed)', torrent_parser_label_cancel_title: 'Uncheck', torrent_parser_label_cancel_descr: 'Remove the mark from the distribution (viewed)', torrent_parser_timeout: 'Timeout expired', torrent_parser_nofiles: 'Failed to extract suitable files', torrent_parser_set_link: 'Specify a link for parsing', torrent_parser_request_error: 'Request error', torrent_parser_magnet_error: 'Failed to get magnet link', torrent_parser_no_responce: 'The parser is not responding to the request', torrent_parser_torlook_fallback_search_notification: 'Jackett parser is not available or the connection settings are incorrect. Searching Torlook...', about_text: 'The application is completely free and uses public links to get information about videos, new releases, popular movies, etc. All available information is used solely for educational purposes, the application does not use its own servers to distribute information.', about_channel: 'Our channel', about_group: 'Group', about_version: 'Version', about_donate: 'Donat', title_watched: 'You watched', title_settings: 'Settings', title_collections: 'Collections', title_company: 'Company', title_actors: 'Actors', title_actor: 'Actor', title_actress: 'Actress', title_person: 'Person', title_persons: 'Persons', title_comments: 'Comments', title_torrents: 'Torrents', title_trailers: 'Trailers', title_watch: 'Watch', title_error: 'Error', title_links: 'Links', title_choice: 'Choose', title_main: 'Home', title_book: 'Bookmarks', title_like: 'Like', title_wath: 'Later', title_history: 'Browsing history', title_mytorrents: 'My torrents', title_last: 'Last', title_action: 'Action', title_action_infuse: 'Infuse action', title_producer: 'Producer', title_collection: 'Collection', title_recomendations: 'Recommendations', title_similar: 'Similar', title_about: 'About the application', title_timetable: 'Schedule', title_relises: 'Digital releases', title_catalog: 'Catalog', title_category: 'Category', title_parser: 'Parser', title_type: 'Type of', title_rating: 'Rating', title_country: 'Country', title_year: 'Year', title_genre: 'Genre', title_filter: 'Filter', title_notice: 'Notifications', title_files: 'Files', title_now_watch: 'Watching now', title_latest: 'Last addition', title_continue: 'Continue browsing', title_recomend_watch: 'We recommend to see', title_new_episodes: 'New episodes', title_popular: 'Popular', title_popular_movie: 'Popular films', title_popular_tv: 'Popular TV shows', title_new_this_year: 'New this year', title_hight_voite: 'Highly rated', title_new: 'New', title_trend_day: 'Today in trend', title_trend_week: 'Trending for the week', title_upcoming: 'Watch in cinemas', title_top_movie: 'Top movies', title_top_tv: 'Top series', title_tv_today: 'On air today', title_this_week: 'This week', title_in_top: 'Top', title_out: 'Exit', title_out_confirm: 'Yes, get out', title_continue_two: 'Proceed', title_choice_language: 'Choose a language', title_subscribe: 'Subscribe', title_subscribes: 'Subscriptions', title_unsubscribe: 'Unsubscribe', title_language: 'Original language', subscribe_success: 'You have successfully subscribed', subscribe_error: 'An error occurred while subscribing, please try again later', subscribe_noinfo: 'Failed to retrieve information, please try again later', company_headquarters: 'Headquarters', company_homepage: 'Website', company_country: 'Country', country_ad: 'Andorra', country_ae: 'UAE', country_af: 'Afghanistan', country_al: 'Albania', country_am: 'Armenia', country_ao: 'Angola', country_ar: 'Argentina', country_at: 'Austria', country_au: 'Australia', country_aw: 'Aruba', country_az: 'Azerbaijan', country_bа: 'Bosnia & Herzegovina', country_bd: 'Bangladesh', country_be: 'Belgium', country_bg: 'Bulgaria', country_bh: 'Bahrain', country_bi: 'Burundi', country_bj: 'Benin', country_bo: 'Bolivia', country_br: 'Brazil', country_bs: 'Bahamas', country_bt: 'Bhutan', country_bw: 'Botswana', country_by: 'Belarus', country_ca: 'Canada', country_ch: 'Switzerland', country_cl: 'Chile', country_cm: 'Cameroon', country_cn: 'China', country_co: 'Colombia', country_cr: 'Costa Rica', country_cu: 'Cuba', country_cv: 'Cape Verde', country_cy: 'Cyprus', country_cz: 'Czech Republic', country_de: 'Germany', country_dj: 'Djibouti', country_dk: 'Denmark', country_do: 'Dominican Republic', country_dz: 'Algeria', country_ec: 'Ecuador', country_ee: 'Estonia', country_eg: 'Egypt', country_es: 'Spain', country_et: 'Ethiopia', country_fi: 'Finland', country_fo: 'Faroe Islands', country_fr: 'France', country_ga: 'Gabon', country_gb: 'United Kingdom', country_ge: 'Georgia', country_gh: 'Ghana', country_gl: 'Greenland', country_gp: 'Guadeloupe', country_gr: 'Greece', country_gt: 'Guatemala', country_hk: 'Hong Kong', country_hr: 'Croatia', country_ht: 'Haiti', country_hu: 'Hungary', country_id: 'Indonesia', country_ie: 'Ireland', country_il: 'Israel', country_in: 'India', country_iq: 'Iraq', country_ir: 'Iran', country_is: 'Iceland', country_it: 'Italy', country_jm: 'Jamaica', country_jo: 'Jordan', country_jp: 'Japan', country_ke: 'Kenya', country_kg: 'Kyrgyzstan', country_kh: 'Cambodia', country_kp: 'North Korea', country_kr: 'South Korea', country_kz: 'Kazakhstan', country_kw: 'Kuwait', country_la: 'Laos', country_lb: 'Lebanon', country_li: 'Liechtenstein', country_lk: 'Sri Lanka', country_lr: 'Liberia', country_lt: 'Lithuania', country_lu: 'Luxembourg', country_lv: 'Latvia', country_ly: 'Libya', country_ma: 'Morocco', country_mc: 'Monaco', country_md: 'Moldova', country_me: 'Montenegro', country_mk: 'Macedonia', country_mm: 'Myanmar', country_mn: 'Mongolia', country_mo: 'Macau', country_mt: 'Malta', country_mu: 'Mauritius', country_mv: 'Maldives', country_mw: 'Malawi', country_mx: 'Mexico', country_my: 'Malaysia', country_mz: 'Mozambique', country_na: 'Namibia', country_ne: 'Niger', country_ng: 'Nigeria', country_ni: 'Nicaragua', country_nl: 'Netherlands', country_no: 'Norway', country_np: 'Nepal', country_nz: 'New Zealand', country_om: 'Oman', country_pa: 'Panama', country_pe: 'Peru', country_pg: 'Papua New Guinea', country_ph: 'Philippines', country_pk: 'Pakistan', country_pl: 'Poland', country_pr: 'Puerto Rico', country_ps: 'Palestinian Territory', country_pt: 'Portugal', country_py: 'Paraguay', country_qa: 'Qatar', country_ro: 'Romania', country_rs: 'Serbia', country_ru: 'Russia', country_rw: 'Rwanda', country_sa: 'Saudi Arabia', country_sd: 'Sudan', country_se: 'Sweden', country_sg: 'Singapore', country_si: 'Slovenia', country_sk: 'Slovakia', country_sn: 'Senegal', country_su: 'USSR', country_sv: 'El Salvador', country_sy: 'Syria', country_th: 'Thailand', country_tj: 'Tajikistan', country_tm: 'Turkmenistan', country_tn: 'Tunisia', country_tr: 'Turkey', country_tw: 'Taiwan', country_tz: 'Tanzania', country_ua: 'Ukraine', country_ug: 'Uganda', country_us: 'USA', country_uy: 'Uruguay', country_uz: 'Uzbekistan', country_ve: 'Venezuela', country_vn: 'Viet Nam', country_xk: 'Kosovo', country_ws: 'Samoa', country_ye: 'Yemen', country_yu: 'Yugoslavia', country_za: 'South Africa', country_zm: 'Zambia', country_zw: 'Zimbabwe', filter_clarify: 'Clarify', filter_clarify_two: 'Refine Search', filter_set_name: 'Specify title', filter_sorted: 'Sort', filter_filtred: 'Filter', filter_any: 'Any', filter_combinations: 'Combinations', filter_alt_names: 'Other names', filter_rating_from: 'from', filter_rating_to: 'to', filter_lang_af: 'Afrikaans', filter_lang_ar: 'Arabic', filter_lang_az: 'Azerbaijani', filter_lang_ba: 'Bashkir', filter_lang_be: 'Belarusian', filter_lang_bg: 'Bulgarian', filter_lang_bn: 'Bengali', filter_lang_bs: 'Bosnian', filter_lang_ca: 'Catalan', filter_lang_ce: 'Chechen', filter_lang_cs: 'Czech', filter_lang_da: 'Danish', filter_lang_de: 'German', filter_lang_el: 'Greek', filter_lang_en: 'English', filter_lang_es: 'Spanish', filter_lang_et: 'Estonian', filter_lang_fa: 'Persian', filter_lang_fi: 'Finnish', filter_lang_fr: 'French', filter_lang_ga: 'Irish', filter_lang_gl: 'Galician', filter_lang_gn: 'Guarani', filter_lang_he: 'Hebrew', filter_lang_hi: 'Hindi', filter_lang_hr: 'Croatian', filter_lang_hu: 'Hungarian', filter_lang_hy: 'Armenian', filter_lang_id: 'Indonesian', filter_lang_is: 'Icelandic', filter_lang_it: 'Italian', filter_lang_ja: 'Japanese', filter_lang_ka: 'Georgian', filter_lang_kk: 'Kazakh', filter_lang_ko: 'Korean', filter_lang_ks: 'Kashmiri', filter_lang_ku: 'Kurdish', filter_lang_ky: 'Kirghiz', filter_lang_lt: 'Lithuanian', filter_lang_lv: 'Latvian', filter_lang_mi: 'Maori', filter_lang_mk: 'Macedonian', filter_lang_mn: 'Mongolian', filter_lang_mt: 'Maltese', filter_lang_ne: 'Nepali', filter_lang_nl: 'Dutch (Nederlands)', filter_lang_no: 'Norwegian', filter_lang_pa: 'Punjabi', filter_lang_pl: 'Polish', filter_lang_ps: 'Pushto', filter_lang_pt: 'Portuguese', filter_lang_ro: 'Romanian', filter_lang_ru: 'Russian', filter_lang_si: 'Sinhalese', filter_lang_sk: 'Slovak', filter_lang_sl: 'Slovenian', filter_lang_sm: 'Samoan', filter_lang_so: 'Somali', filter_lang_sq: 'Albanian', filter_lang_sr: 'Serbian', filter_lang_sv: 'Swedish', filter_lang_sw: 'Swahili', filter_lang_ta: 'Tamil', filter_lang_tg: 'Tajik', filter_lang_th: 'Thai', filter_lang_tk: 'Turkmen', filter_lang_tr: 'Turkish', filter_lang_tt: 'Tatar', filter_lang_ur: 'Urdu', filter_lang_uk: 'Ukrainian', filter_lang_uz: 'Uzbek', filter_lang_vi: 'Vietnamese', filter_lang_yi: 'Yiddish', filter_lang_zh: 'Chinese', filter_genre_ac: 'Action', filter_genre_ad: 'Adventures', filter_genre_mv: 'Cartoon', filter_genre_cm: 'Comedy', filter_genre_cr: 'Crime', filter_genre_dc: 'Documentary', filter_genre_dr: 'Drama', filter_genre_fm: 'Family', filter_genre_fe: 'Fantasy', filter_genre_hi: 'Story', filter_genre_ho: 'Horror', filter_genre_mu: 'Music', filter_genre_de: 'Detective', filter_genre_md: 'Melodrama', filter_genre_fa: 'Fiction', filter_genre_tv: 'TV film', filter_genre_tr: 'Thriller', filter_genre_mi: 'Military', filter_genre_ve: 'Western', filter_genre_aa: 'Action & Adventure', filter_genre_ch: 'Children\'s', filter_genre_nw: 'News', filter_genre_rs: 'Reality show', filter_genre_hf: 'Sci-Fi and Fantasy', filter_genre_op: 'Soap opera', filter_genre_tc: 'Talk show', filter_genre_mp: 'War and Politics', empty_title: 'Empty', empty_text: 'Nothing found for your filter, please refine your filter.', empty_title_two: 'It\'s empty here', empty_text_two: 'The list is currently empty.', menu_main: 'Home', menu_movies: 'Movies', menu_tv: 'Series', menu_catalog: 'Catalog', menu_filter: 'Filter', menu_collections: 'Collections', menu_relises: 'Releases', menu_anime: 'Anime', menu_bookmark: 'Bookmarks', menu_like: 'Like', menu_time: 'Later', menu_history: 'History', menu_timeline: 'Schedule', menu_torrents: 'Torrents', menu_settings: 'Settings', menu_about: 'Information', menu_console: 'Console', menu_multmovie: 'Cartoons', menu_multtv: 'Animated series', plugins_catalog_work: 'Working plugins', plugins_catalog_work_descr: 'Plugins that work exactly in the lamp.', plugins_catalog_popular: 'Popular plugins among users', plugins_catalog_popular_descr: 'Installation from unknown sources may cause the application to work incorrectly.', plugins_online: 'View online', plugins_check_fail: 'Failed to test the functionality of the plugin. However, this does not mean that the plugin does not work. Reload the application to see if the plugin is loading.', plugins_need_reload: 'To apply the plugin, you need to restart the application', plugins_install: 'Install', plugins_install_ready: 'This plugin is already installed.', plugins_installed: 'Installations', plugins_load_from: 'Loaded from CUB', plugins_ok_for_check: 'Click (OK) to test the plugin', plugins_no_loaded: 'When loading the application, some plugins could not be loaded', time_viewed: 'Viewed', time_from: 'from', time_reset: 'Reset timecode', settings_clear_cache: 'Cache and data cleared', settings_user_links: 'Custom Link', settings_for_local: 'For local TorrServer', settings_add: 'Add', settings_remove: 'Delete', settings_this_value: 'present value', settings_added: 'Added', settings_removed: 'Removed', settings_param_player_inner: 'Lampa', settings_param_player_outside: 'External', settings_param_player_inner_tvos: 'Internal', settings_param_yes: 'Yes', settings_param_no: 'No', settings_param_interface_size_small: 'Smaller', settings_param_interface_size_normal: 'Normal', settings_param_interface_size_bigger: 'More', settings_param_poster_quality_low: 'Low', settings_param_poster_quality_average: 'Average', settings_param_poster_quality_high: 'High', settings_param_parse_directly: 'Directly', settings_param_parse_api: 'Through the website API', settings_param_background_complex: 'Complex', settings_param_background_simple: 'Simple', settings_param_background_image: 'Picture', settings_param_link_use_one: 'Main', settings_param_link_use_two: 'Additional', settings_param_link_use_both: 'Both', settings_param_subtitles_size_small: 'small', settings_param_subtitles_size_normal: 'Ordinary', settings_param_subtitles_size_bigger: 'Large', settings_param_screensaver_nature: 'Nature', settings_param_torrent_lang_orig: 'Original', settings_param_torrent_lang_ru: 'Russian', settings_param_player_timecode_again: 'Start over', settings_param_player_timecode_continue: 'Proceed', settings_param_player_timecode_ask: 'To ask', settings_param_player_scale_method: 'Calculate', settings_param_player_hls_app: 'Systemic', settings_param_player_hls_js: 'Program', settings_param_card_view_load: 'Upload', settings_param_card_view_all: 'Show all', settings_param_navigation_remote: 'Remote controller', settings_param_navigation_mouse: 'Remote control with mouse', settings_param_keyboard_lampa: 'Lampa', settings_param_keyboard_system: 'Systemic', helper_keyboard: 'After entering the value, press the "Back" button to save', helper_torrents: 'Hold down the (OK) key to bring up the context menu', helper_cleared: 'Success, tooltips will be shown again.', helper_torrents_view: 'Hold down the (OK) key to reset the timecode and display the menu', fav_sync_title: 'Bookmark sync', fav_sync_text: 'Your favorite bookmarks with you. Connect synchronization and view on any device.

    To do this, register on the site {site}, create a profile and log in to the application.', fav_sync_site: 'Website', fav_remove_title: 'Remove from history', fav_remove_descr: 'Delete selected card', fav_clear_title: 'Clear the history', fav_clear_descr: 'Delete all cards from history', fav_clear_label_title: 'Clear labels', fav_clear_label_descr: 'Clear View Tags', fav_clear_time_title: 'Clear Timecodes', fav_clear_time_descr: 'Clear all timecodes', fav_label_cleared: 'Marks cleared', fav_time_cleared: 'Timecodes cleared', timetable_empty: 'This section will display the release dates of new episodes.', player_quality: 'Quality', player_flow: 'Flow', player_tracks: 'Audio tracks', player_disabled: 'Disabled', player_unknown: 'Unknown', player_subs: 'Subtitles', player_size_default_title: 'Default', player_size_default_descr: 'Default video size', player_size_cover_title: 'Expand', player_size_cover_descr: 'Expands video to full screen', player_size_fill_title: 'Fill', player_size_fill_descr: 'Fit video to full screen', player_size_s115_title: 'Zoom 115%', player_size_s115_descr: 'Enlarge video by 115%', player_size_s130_title: 'Zoom 130%', player_size_s130_descr: 'Enlarge video by 130%', player_size_v115_title: 'Vertical 115%', player_size_v115_descr: 'Enlarge video by 115%', player_size_v130_title: 'Vertical 130%', player_size_v130_descr: 'Enlarge video by 130%', player_video_size: 'Video size', player_playlist: 'Playlist', player_error_one: 'Failed to decode video', player_error_two: 'Video not found or corrupted', player_start_from: 'Continue browsing from', player_not_found: 'Player not found', player_lauch: 'Launch player', player_speed_default_title: 'Plain', player_speed_two_descr: 'Play without sound', player_video_speed: 'Playback speed', player_share_title: 'Share', player_share_descr: 'Play this video on another device', player_normalization_power_title: 'Normalization power', player_normalization_smooth_title: 'Normalization speed', player_normalization_step_low: 'Low', player_normalization_step_medium: 'Average', player_normalization_step_hight: 'High', player_youtube_no_played: 'Sorry, this video is not available in your region and may have been blocked or removed.', player_youtube_start_play: 'To start playing the video, click the "Play" button.', broadcast_open: 'Open card on another device', broadcast_play: 'Choose the device to watch on', card_new_episode: 'New series', card_book_remove: 'Remove from bookmarks', card_book_add: 'To bookmarks', card_book_descr: 'Look in the menu (Bookmarks)', card_like_remove: 'Remove from favorites', card_like_add: 'Like', card_like_descr: 'Look at the menu (Like)', card_wath_remove: 'Remove from expected', card_wath_add: 'Watch Later', card_wath_descr: 'See the menu (Later)', card_history_remove: 'Remove from history', card_history_add: 'Into history', card_history_descr: 'Look in the menu (History)', keyboard_listen: 'Speak, I\'m listening...', keyboard_nomic: 'No microphone access', notice_new_quality: 'New quality available', notice_quality: 'Quality', notice_new_episode: 'New series', notice_none: 'You don\'t have any notifications yet, register at {site} to follow new episodes and releases.', notice_in_quality: 'As', notice_none_account: 'You don\'t have any notifications yet, bookmark the series and wait for notifications of new episodes.', notice_none_system: 'You currently have no notifications. We will be sure to notify you when new notifications become available.', copy_link: 'Copy video link', copy_secuses: 'Link copied to clipboard', copy_error: 'Error copying link', account_sync_to_profile: 'All bookmarks will be moved to the profile', account_sync_secuses: 'All bookmarks have been successfully transferred', account_profiles: 'Profiles', account_profiles_empty: 'Failed to get list of profiles', account_authorized: 'Authorized', account_logged_in: 'You are logged in', account_login_failed: 'Login failed', account_login_wait: 'Waiting for login', account_profile_main: 'General', account_export_secuses: 'Export completed successfully', account_export_fail: 'Export error', account_import_secuses: 'Import completed successfully', account_import_fail: 'Import error', account_imported: 'imported', account_reload_after: 'reboot after 5 sec.', account_create: 'Discover more opportunities with a CUB account. Register at and get access to sync your bookmarks, timecodes and other CUB account features.', account_premium_more: 'Learn more about CUB Premium', account_limited: 'You have reached the maximum limit. Increase the limit with a CUB Premium account. Learn more at ', account_code_enter: 'Enter a six-digit code', account_code_error: 'Perhaps you entered an incorrect or outdated code', account_code_wrong: 'Perhaps you entered an incorrect format', account_code_where: 'Go to the website and enter the code provided there.', account_code_input: 'Enter code', settings_cub_signin_button: 'Sign in', network_noconnect: 'No network connection', network_404: 'The requested page was not found. [404]', network_401: 'Authorization failed', network_500: 'Internal Server Error. [500]', network_parsererror: 'The requested JSON parsing failed.', network_timeout: 'Request timed out.', network_abort: 'The request has been aborted.', network_error: 'Unknown error', size_zero: '0 Byte', size_byte: 'Byte', size_kb: 'KB', size_mb: 'MB', size_gb: 'GB', size_tb: 'TB', size_pp: 'PB', speed_bit: 'bps', speed_kb: 'Kbps', speed_mb: 'Mbps', speed_gb: 'Gbps', speed_tb: 'Tbps', speed_pp: 'Pbps', month_1: 'January', month_2: 'February', month_3: 'March', month_4: 'April', month_5: 'May', month_6: 'June', month_7: 'July', month_8: 'August', month_9: 'September', month_10: 'October', month_11: 'November', month_12: 'December', day_1: 'Monday', day_2: 'Tuesday', day_3: 'Wednesday', day_4: 'Thursday', day_5: 'Friday', day_6: 'Saturday', day_7: 'Sunday', month_1_e: 'January', month_2_e: 'February', month_3_e: 'March', month_4_e: 'April', month_5_e: 'May', month_6_e: 'June', month_7_e: 'July', month_8_e: 'August', month_9_e: 'September', month_10_e: 'October', month_11_e: 'November', month_12_e: 'December', week_1: 'Mon', week_2: 'Tue', week_3: 'Wed', week_4: 'Thu', week_5: 'Fri', week_6: 'Sat', week_7: 'Sun', extensions_enable: 'Turn on', extensions_disable: 'Disable', extensions_check: 'Check status', extensions_install: 'Install', extensions_info: 'Information', extensions_edit: 'Edit', extensions_change_name: 'Change name', extensions_change_link: 'Change link', extensions_remove: 'Delete', extensions_set_name: 'Enter plugin name', extensions_set_url: 'Enter plugin url', extensions_ready: 'This plugin is already installed', extensions_no_info: 'Without information', extensions_no_name: 'Untitled', extensions_worked: 'Worker', extensions_no_plugin: 'Plugin not verified', extensions_add: 'Add Plugin', extensions_from_memory: 'Installed in memory', extensions_from_cub: 'Installed from CUB', extensions_from_popular: 'Popular plugins', extensions_from_lib: 'Plugin Library', extensions_from_connected: 'Connected plugins', settings_webos_launcher: 'Application launch', settings_webos_launcher_add_device: 'Install as starter app', settings_webos_launcher_remove_device: 'Remove from starter spplications', player_normalization: 'Normalization', change_source_on_cub: 'Change source to CUB', settings_param_jackett_interview_all: 'All', settings_param_jackett_interview_healthy: 'Available only', settings_parser_jackett_interview: 'Poll trackers', title_ongoing: 'Ongoings', title_pgrating: 'Age limit', settings_interface_card_interfice: 'Card interface', settings_interface_card_poster: 'Show poster', title_card: 'Card', settings_param_card_interface_old: 'Old', settings_param_card_interface_new: 'New', title_seasons: 'Seasons', title_episodes: 'Episodes', title_rewiews: 'Reviews', settings_interface_glass: 'Glass', settings_interface_glass_descr: 'Show interface in glassy style', settings_interface_black_style: 'Black style', plugins_remove: 'Remove plugins', plugins_add_success: 'Plugin connected successfully', settings_reset: 'Reset', title_channel: 'Channel', input_detection_touch: 'Want to switch to touch control?', input_detection_mouse: 'Want to switch to mouse control?', input_detection_remote: 'Want to switch to remote control?', settings_interface_hide_outside_the_screen: 'Hide cards off screen', settings_interface_hide_outside_the_screen_descr: 'This will speed up UI rendering and improve performance', https_text: 'You are using the HTTPS protocol, in this protocol the lamp does not work correctly. For the correct operation of the lamp, use the address with the HTTP protocol', extensions_hpu_best: 'Popular', extensions_hpu_recomend: 'Recommended', extensions_hpu_theme: 'Themes', extensions_hpu_screensaver: 'Screensaver', extensions_hpu_video: 'Video', extensions_hpu_control: 'Control', extensions_hpu_other: 'Miscellaneous', extensions_hpu_: 'Other', title_author: 'Author', title_buffer: 'Buffer', settings_rest_screensaver_time: 'After how many minutes to start the screensaver', time_h: 'h.', time_m: 'm.', time_s: 's.', settings_param_glass_easy: 'Transparent', settings_param_glass_medium: 'Semitransparent', settings_param_glass_blacked: 'Blacked out', settings_interface_glass_opacity: 'Glass transparency', torrent_error_check_no_auth: 'The server responded to the request, but the authorization failed', settings_interface_card_cover: 'Show cover', title_upcoming_episodes: 'Upcoming episode releases', title_recent_episodes: 'Recent episode releases', settings_rest_cache_images: 'Image cache', settings_rest_cache_images_descr: 'Cache posters and backgrounds to local storage', settings_player_rewind_title: 'Rewind', settings_player_rewind_descr: 'Rewind interval in seconds', settings_interface_card_reactions: 'Show reactions', title_look: 'Looking', title_scheduled: 'Scheduled', title_viewed: 'Viewed', title_thrown: 'Thrown', title_reactions: 'Reactions', reactions_none: 'No reactions', reactions_fire: 'Great', reactions_nice: 'Nice', reactions_think: 'Interesting', reactions_bore: 'Boring', reactions_shit: 'Bad', reactions_ready: 'You have already left a reaction', settings_player_launch_trailers: 'Trailer Player', title_continued: 'To be continued', title_language_short: 'Language', speedtest_connect: 'connection', speedtest_test: 'testing', speedtest_ready: 'ready', speedtest_button: 'Test Speed', termsofuse_t_01: 'Terms of Use', termsofuse_t_02: 'Lampa is a convenient tool for exploring the latest news in the film industry, including movies, TV shows, cartoons, and other information. To obtain information about movies, an open-source website, themoviedb.org, is used.', termsofuse_t_03: 'Content', termsofuse_t_04: 'Lampa utilizes the API (application programming interface) from the themoviedb.org catalog to retrieve information about movies and TV shows, as well as to view associated media files such as posters, trailers, and more.', termsofuse_t_05: 'The themoviedb.org API allows the Lampa application to access a database of movies and TV shows, providing the ability to search and view media file information.', termsofuse_t_06: 'The interaction between the Lampa application and themoviedb.org occurs through API requests that are sent from the Lampa application to the themoviedb.org servers to obtain information and media files. Themoviedb.org processes the requests and sends the requested data back to the Lampa application.', termsofuse_t_07: 'Thus, thanks to the use of the themoviedb.org API, the Lampa application provides users with access to an extensive database of movies and TV shows, while complying with the terms of use and restrictions set by the rights holder.', termsofuse_t_08: 'Extensions', termsofuse_t_09: 'Extensions are a powerful tool provided for the Lampa application, allowing users to enhance its capabilities, add additional functionality, and modify content according to their needs.', termsofuse_t_10: 'However, it should be noted that users install extensions at their own risk. All consequences arising from the use of extensions are the responsibility of the user. Users are advised to exercise caution when installing and using extensions, carefully evaluating their reliability and security.', termsofuse_t_11: 'Copyright Infringements', termsofuse_t_12: 'If you discover any copyright infringement in the Lampa application, you should contact the developers of the Lampa application. You can use the contacts provided in the Lampa application to do so. However, it is important to note that the Lampa application uses the API from the themoviedb.org catalog, which provides open access to information about movies and TV shows. This means that the information displayed in the Lampa application is publicly available and does not infringe on copyright.', termsofuse_t_13: 'If you believe that the Lampa application still contains content that violates your copyright, you can contact the rights holder of that content and request its removal from the themoviedb.org website. In turn, the Lampa application can remove content upon receiving a proper copyright infringement notification.', settings_rest_protocol_use: 'Secure connection', settings_rest_protocol_descr: 'Use HTTPS protocol', install_app_apk_title: 'How to install Lampa on Android', install_app_apk_text: 'We noticed that you use MediaStationX to run Lampa on Android. We recommend installing Lampa as an APK app. This will ensure more stable operation and access to all the app\'s features.

    Go to the Telegram group to download the latest APK and installation instructions.', install_app_apk_qr: 'Scan the QR code to download the Lampa APK', copy_link_buffer: 'Copy link', tv_status_returning_series: 'Returning series', tv_status_planned: 'Planned', tv_status_in_production: 'In Production', tv_status_ended: 'Ended', tv_status_canceled: 'Canceled', tv_status_pilot: 'Pilot', tv_status_released: 'Released', tv_status_rumored: 'Rumored', tv_status_post_production: 'Post Production', title_last_year: 'Last year', title_reset: 'Reboot', back_to_card: 'Return to card', premiere_author_recomend_1: 'Order pizza and get ready for some excitement.', premiere_author_recomend_2: 'The party is just getting started.', premiere_author_recomend_3: 'Get ready for some adrenaline.', premiere_author_recomend_4: 'Ready for an unforgettable evening?', premiere_author_recomend_5: 'I recommend watching it.', premiere_title: 'Premiere', settings_clear_cache_only: 'Cache Cleared', settings_rest_cache_only: 'Only Cache', settings_rest_cache_only_descr: 'Basic settings will be preserved', settings_rest_cache_all: 'Cache and Data', settings_rest_cache_all_descr: 'All settings and data will be cleared', settings_rest_cache_calculate: 'How much memory is left', title_left: 'Left', account_export_fail_500: 'Unknown error', account_export_fail_610: 'You have exceeded the limit for today', account_export_fail_200: 'Database error, please try again later', account_export_fail_600: 'Error, failed to upload the file', account_export_fail_620: 'Error, file not transmitted', title_parental_control: 'Parental Control', settings_parental_control_param_time_always: 'Always', settings_parental_control_param_time_once: 'Once', settings_parental_control_param_time_10: 'In 10 minutes', settings_parental_control_param_time_20: 'In 20 minutes', settings_parental_control_param_time_30: 'In 30 minutes', settings_parental_control_param_time_60: 'In 1 hour', settings_parental_control_param_time_120: 'In 2 hours', settings_parental_control_change_pin: 'Change PIN', settings_parental_control_demand_title: 'When to Prompt', settings_parental_control_demand_descr: 'After how much time to prompt for the PIN', settings_parental_control_enabled: 'Enabled', settings_parental_control_disabled: 'Disabled', parental_control_input_new_code: 'Enter new PIN code', parental_control_confirm_new_code: 'Confirm PIN code', parental_control_no_match_code: 'PIN code does not match the previous code', parental_control_input_code: 'Enter PIN code', parental_control_input_error: 'You entered an incorrect PIN code', title_worth_rewatch: 'Worth a rewatch', ad_disable: 'By subscribing to CUB Premium, you will forget about advertising.', ad_after: 'Advertising via', ad: 'Advertising', ad_skip: 'Skip', ad_continue_after: 'Viewing will start in', settings_parser_prowlarr_link_descr: 'Provide a link to the Prowlarr server', settings_parser_prowlarr_key_descr: 'Located in the Prowlarr settings tab in the general category', settings_player_torrent_type: 'Player type for torrents', settings_player_torrent_type_descr: 'Which player to play torrents', full_crew: 'More', full_editing: 'Editing', full_creator: 'Creator', player_normalization_step_none: 'Zero', settings_cub_backup_export_descr: 'Save settings to the cloud', settings_cub_backup_import_descr: 'Restore settings from the cloud', settings_interface_sound: 'System sounds', settings_interface_sound_play: 'Transitions', settings_interface_sound_level: 'Volume level', settings_interface_advanced_animation: 'Advanced animation', confirm_open_player: 'Another device is requesting access to the player. Do you want to play the video?', pincode_use_remote: 'Use the remote control to enter the code by pressing the arrows to select the desired number.', pincode_use_toggle: 'Press and hold the OK button to change the layout.', settings_parental_control_where: 'Where to request a PIN code', title_fire: 'Fire!', title_top_100: 'Top 100', player_normalization_type_title: 'Normalization Type', player_normalization_type_all: 'All', player_normalization_type_up: 'Only Increase', player_normalization_type_down: 'Only Decrease', storage_quota_exceeded: 'Storage quota exceeded. Clear cache and try again.', settings_cub_domain: 'Primary domain', title_ai_assistant: 'AI Assistant', title_facts: 'Interesting Facts', ai_search_start_typing: 'Ask the AI Assistant what movies you want to find. For example: "Movies about space"', ai_search_limit: 'Request limit exceeded. Please try again later. With a CUB Premium subscription, limits will be increased, and you won’t have to wait!', ai_subscribe_title: 'Unlock More Opportunities with AI', ai_subscribe_descr: 'Subscribe to CUB Premium and increase the number of requests to AI. Together with AI, we’ll find the best movies for you.', dmca_title: 'Content blocked', dmca_descr: 'This material is not available for viewing. It has been blocked at the request of the copyright holder in accordance with copyright law.', loading_progress_step_0: 'Preparation', loading_progress_step_1: 'Loading languages', loading_progress_step_2: 'Searching for mirrors', loading_progress_step_3: 'Preparing extensions', loading_progress_step_4: 'Almost there', loading_progress_step_5: 'Coming soon', install_extension_rule_1: 'Installing a third-party plugin', install_extension_rule_2: 'Plugins are created by independent developers, and LAMPA does not verify their content. By installing them, you agree that:', install_extension_rule_3: '- Some plugins may request access to your data or work with unverified sources.', install_extension_rule_4: '- If the plugin you install violates copyright or other laws, you are fully responsible for its use.', install_extension_rule_5: '- Install only plugins from trusted sources. If you have any doubts about the security or legality of a plugin, it is better to refrain from installing it.', install_extension_rule_6: 'LAMPA does not support or endorse plugins related to pirated content.', install_extension_rule_7: 'By proceeding, you confirm that you understand these risks.', ad_plugin: 'Advertisement from the plugin', terminal_title: 'Terminal', terminal_no_access: 'No access', terminal_confirm: 'Allow', terminal_deny: 'Deny', terminal_update: 'Update', terminal_code: 'Access code', terminal_text: 'You are requesting access to the terminal. Developers may gain access to your device and control it. This can be useful for diagnostics and troubleshooting, but may also pose a security risk. Please make sure you trust the developers before granting access.

    You can disable this feature at any time by clicking the "Deny" button.', title_anons: 'Announcement', title_developer: 'Developer', title_navigation: 'Navigation', title_page: 'Page', title_person_about: 'About', account_qr_premium: 'Scan the QR code to learn more about CUB Premium', account_qr_code: 'Scan the QR code to receive a device linking code', account_qr_create: 'Scan the QR code to create an account', account_add_device_title: 'Add Device', account_add_device_text: 'Go to to get a code for adding a device.', account_none_title: 'Don’t have an account yet?', account_none_include_1: 'Bookmarks and watch history are stored in the cloud', account_none_include_2: 'Sync between devices', account_none_include_3: 'Notifications about new episodes and releases', account_none_include_4: 'Create multiple profiles under one account', account_premium_include_1: 'Increased number of bookmarks', account_premium_include_2: 'Extended watch history', account_premium_include_3: 'More timecodes available', account_premium_include_4: 'Number of profiles per account', empty_subscribes_title: 'Your translation subscriptions', empty_subscribes_text: 'Just subscribe to your favorite translation and we’ll notify you when a new episode is released.', empty_history_title: 'Your watch history', empty_history_text: 'Your watch history will appear here.', empty_bookmark_title: 'Your favorites', empty_bookmark_text: 'Add movies and TV shows you like to favorites to quickly find them in this section.', empty_mytorrents_title: 'Your torrents', empty_mytorrents_text: 'Torrents you’ve downloaded via TorServer will appear here.', developer_param_enabled: 'Developer Mode', developer_param_nopremium: 'Disable CUB Premium', developer_param_nodemo: 'Disable Demo Mode', developer_param_ads: 'Show Ads', developer_param_fps: 'Show FPS', developer_trigger_help_1: 'Almost guessed it :)', developer_trigger_help_2: 'Just a little more :)', developer_trigger_help_3: 'Are you sure you want to enter Developer Mode?', developer_trigger_help_4: 'Developer Mode activated', settings_param_navigation_touch: 'Sensor', account_discuss_add_qr: 'Scan the QR code', account_discuss_add_title: 'Want to leave a comment?', account_discuss_add_text: 'Scan the QR code to go to the app and add a comment on this movie or TV series.

    - Take your phone or tablet
    - Open the camera
    - Point to the QR code
    - Follow the link that appears on the screen', account_discuss_added: 'Thank you for your comment!', account_discuss_added_ready: 'You have already left a comment on this movie or TV series.', remote_helper_long: 'Hold the (OK) button to open the menu', account_premium: 'Unlock new horizons with a CUB Premium account! Enjoy increased limits and enhanced service functionality. Visit and learn more about the benefits of a premium account.', player_segments_title: 'Segments', player_segments_descr: 'Skip intro and other parts of the video', player_segments_ad_title: 'Ads', player_segments_skip_title: 'Intro and other', player_segments_value_none: 'Do not skip', player_segments_value_auto: 'Auto skip', player_segments_value_user: 'Custom', player_segments_skiped: 'Segment skipped', player_segments_skip_now: 'Skip', player_segments_skip_in: 'Skip in', player_segments_skip_intro: 'Skip intro', player_segments_next: 'Next episode', player_segments_next_in: 'Next episode in', subscribe_info: 'In the Subscriptions section, you\'ll see all your translation subscriptions. You\'ll receive a notification when a new episode with your selected translation is released.', no_watch_history: 'No browsing history', settings_request_caching_use: 'Request Caching', settings_request_caching_descr: 'Cache API requests to speed up the interface and reduce data usage.', account_profile_child: 'Child', account_profile_add: 'Add profile', account_profile_name: 'Profile name', account_profile_created: 'Profile successfully created', account_profile_limited: 'The maximum profile limit has been reached: {count}', account_profile_info: 'You can manage profiles in your account settings. To do this, go to ', player_youtube_no_support: 'Your device does not support YouTube video playback, please check your internet connection.', title_channels: 'Channels', title_best_of_2000: 'Golden 2000s', title_best_of_2010: 'Hits of the 2010s', title_best_of_2015: 'Modern Classics', title_comedy_of_2000: 'Laughter of the 2000s', title_comedy_of_2010: 'Humor of the 2010s', title_comedy_of_2015: 'Modern Comedies', filter_keyword_sequel: 'Sequel', filter_keyword_based_comic: 'Based on Comics', filter_keyword_space: 'Space', filter_keyword_survival: 'Survival', filter_keyword_zombie: 'Zombie', filter_keyword_school: 'School', filter_keyword_revenge: 'Revenge', filter_keyword_detective: 'Detective', filter_keyword_robots: 'Robots', filter_keyword_vampires: 'Vampires', filter_keyword_aliens: 'Aliens', filter_keyword_police: 'Police', filter_keyword_reincarnation: 'Reincarnation', filter_keyword_spy: 'Spy', filter_keyword_disaster: 'Catastrophe', filter_keyword_christmas: 'Christmas', dmca_title_lgbt: 'Content Restricted', dmca_descr_lgbt: 'Viewing is unavailable in your region as it may contain LGBT content. Such material may be prohibited or restricted in some countries.', settings_lgbt_content_block: 'Block LGBT content', // todo: adult_content_title: 'Adult Content', adult_content_text_warning: 'Content may contain adult material that may be inappropriate for some viewers.', adult_content_text_modal: 'This film or series is marked as containing adult content. You must be 18 years of age or older to view this material. Please confirm that you are of legal age to continue viewing.', adult_content_confirm: 'I am 18 or older', adult_content_deny: 'I am under 18', inner_player_disclaimer_title: 'Player Disclaimer', inner_player_disclaimer_text: 'I understand that I am launching a player with incomplete support, and I understand that this may cause atypical behavior on the device. It is recommended to switch to tvOS family players or other alternatives.', inner_player_disclaimer_change_player: 'Change player', remote_configuration_settings_title: 'Remote Configuration', remote_configuration_registration_text: 'Remote configuration provides temporary access to your application, another application will be able to install extensions and configure parameters.', remote_configuration_waite_text: 'You can now pass this code to another application to grant access to settings.', remote_configuration_open_button: 'Open Access', remote_configuration_code_life: 'Code is valid', remote_configuration_notice_nocode: 'Failed to open access', settings_server_gts: 'Transcoding', settings_server_gts_descr: 'Automatically convert video to a compatible format when needed.', settings_server_tracktimecode: 'Enable TrackTimecode', settings_server_tracktimecode_descr: 'Saves the playback progress of movies and TV shows on the TorrServer. This option must also be enabled in TorrServer for it to work.', title_metadata: 'Metadata', title_moods: 'Moods', title_meta_violence: 'Violence', title_meta_fear: 'Fear', title_meta_profanity: 'Profanity', title_meta_sadness: 'Sadness', title_meta_sex: 'Sexuality', player_pip: 'Minimize', player_volume: 'Volume', player_time_end: 'Ends at', player_ai_agent_info: 'Want to know more about the video? Ask the AI assistant and it will provide you with a mood chart, brief plot summary, and highlights.', player_ai_agent_ask_plot: 'Brief plot', player_ai_agent_ask_moods: 'Show chart', player_ai_agent_ask_highlights: 'Highlights', player_ai_agent_ask: 'Ask', player_ai_agent_processing: 'Analyzing...', player_ai_agent_no_data: 'No data to show.', player_ai_agent_no_analysis: 'Could not analyze the video.', player_ai_agent_quota: 'Request limit exceeded. Please try again later.', player_ai_agent_working: 'Still working on it...', title_meta_humor: 'Humor', title_meta_tension: 'Tension', title_meta_romance: 'Romance', title_meta_pace: 'Pace', title_meta_importance: 'Key scenes', title_meta_action: 'Action' }; var langs = {}; var keys = {}; var startup = { uk: ['Підготовка', 'Завантаження мов'], be: ['Падрыхтоўка', 'Загрузка моў'], zh: ['准备', '加载语言'], pt: ['Preparação', 'Carregando idiomas'], bg: ['Подготовка', 'Зареждане на езици'], he: ['הכנה', 'טעינת שפות'], cs: ['Příprava', 'Načítání jazyků'], ro: ['Inițializare', 'Încărcare limbi'], fr: ['Préparation', 'Chargement des langues'], pl: ['Przygotowanie', 'Ładowanie języków'], es: ['Preparación', 'Cargando idiomas'] }; var lang_default = 'ru'; Object.defineProperty(langs, 'ru', { get: function get() { return ru; } }); Object.defineProperty(langs, 'en', { get: function get() { return en; } }); for (var code in meta.languages) { keys[code] = meta.languages[code].name; if (!langs[code]) langs[code] = {}; langs[code].lang_choice_title = meta.languages[code].lang_choice_title; langs[code].lang_choice_subtitle = meta.languages[code].lang_choice_subtitle; } /** * Перевести * @param {string} name * @param {string} custom_code - ru/en/uk... * @returns */ function translate(name, custom_code) { name = name + ''; var code = custom_code || Storage.get('language', 'ru'); var result = ''; if (!langs[code]) code = lang_default; if (name.indexOf('#{') >= 0) { result = name.replace(/#{([a-z_0-9-]+)}/g, function (e, s) { return langs[code][s] || langs[lang_default][s] || s; }); } else { var _startup$code; var step = name === 'loading_progress_step_0' ? 0 : name === 'loading_progress_step_1' ? 1 : null; result = langs[code][name] || (step !== null ? (_startup$code = startup[code]) === null || _startup$code === void 0 ? void 0 : _startup$code[step] : null) || langs[lang_default][name] || name; } result = result.replace(/{site}/g, object$2.cub_site); result = result.replace(/{mirror}/g, Utils$1.protocol() + object$2.cub_domain); return result; } /** * Добавить переводы * @param {{key_name:{en:string,ru:string}}} data */ function add$1(data) { for (var name in data) { for (var _code in data[name]) { if (langs[_code]) { langs[_code][name] = data[name][_code]; } } } } /** * Добавить перевод для кода * @param {string} code * @param {{key1:string,key2:string}} data */ function AddTranslation(code, data) { if (!langs[code]) langs[code] = {}; for (var name in data) { langs[code][name] = data[name]; } } /** * Добавить коды * @param {{code_name:string}} new_codes */ function addCodes(new_codes) { for (var i in new_codes) { keys[i] = new_codes[i]; langs[i] = {}; } } /** * Получить список кодов * @returns {{ru:string,en:string}} */ function codes() { return Arrays.clone(keys); } function selected(check_codes) { return check_codes.indexOf(Storage.get('language', 'ru')) >= 0 ? true : false; } var Lang = { translate: translate, add: add$1, codes: codes, addCodes: addCodes, AddTranslation: AddTranslation, selected: selected }; /** * Инициализирует основное приложение * @returns {void} */ function app() { var app = $('#app').empty(); var wrap = Template.get('wrap'); app.append(Template.get('icons_sprite')); wrap.find('.wrap__left').append(Menu$1.render()); wrap.find('.wrap__content').append(Activity.render()); app.append(Background.render()); app.append(Head.render()); app.append(wrap); app.append(Settings.render()); app.append(Search.render()); } var Render = { app: app }; var html$7; var object; function init$s() { html$7 = Template.get('iframe'); } function show$2() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; object = params; html$7.find('iframe').attr('src', params.url)[0].onload = function () { html$7.addClass('iframe--loaded'); }; $('body').append(html$7); toggle$1(); } function toggle$1() { Controller.add('iframe', { toggle: function toggle() {}, back: close$2 }); Controller.toggle('iframe'); } function close$2() { html$7.removeClass('iframe--loaded'); html$7.detach(); html$7.find('iframe').attr('src', ''); if (object.onBack) object.onBack(); } function render$1() { return html$7; } var Iframe = { init: init$s, show: show$2, close: close$2, render: render$1 }; var items = { App: [], Errors: [], Warnings: [] }; var original = { App: [], Errors: [], Warnings: [] }; var times = 0; var html$6; var scroll_tabs; var scroll_body; var last_tab; var console_orig = { log: console.log, error: console.error, warn: console.warn }; function init$r() { Keypad.listener.follow('keydown', function (e) { if (e.code == 38 || e.code == 29460) { var enable = Controller.enabled(); if (enable.name == 'head') { times++; if (times > 10) { Controller.toggle('console'); } } else { times = 0; } } }); Controller.add('console', { toggle: function toggle() { build(); Controller.toggle('console-tabs'); }, back: back }); Controller.add('console-tabs', { toggle: function toggle() { Controller.collectionSet(scroll_tabs.render()); Controller.collectionFocus(scroll_tabs.render().find('.console__tab[data-name="' + Utils$1.hash(last_tab) + '"]')[0], scroll_tabs.render()); }, down: function down() { Controller.toggle('console-body'); }, right: function right() { Navigator.move('right'); }, left: function left() { Navigator.move('left'); }, back: back }); follow(); } function back() { times = 0; scroll_tabs.destroy(); scroll_body.destroy(); html$6.remove(); Controller.toggle('head'); } function show$1(name) { scroll_body.clear(); scroll_body.reset(); if (items[name]) { items[name].forEach(function (element) { var item = $(element); item.on('hover:focus', function (e) { scroll_body.update($(e.target)); }); if (name == 'Request') { item.on('hover:enter', function (e) { var str = item.text(); var mth = str.match(/error of (.*?) :/); if (mth && mth[1]) { Iframe.show({ url: mth[1], onBack: function onBack() { Controller.toggle('console-body'); } }); } }); } scroll_body.append(item); }); } Controller.add('console-body', { toggle: function toggle() { Controller.collectionSet(scroll_body.render()); Controller.collectionFocus(false, scroll_body.render()); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('console-tabs'); }, down: function down() { Navigator.move('down'); }, back: back }); Controller.toggle('console-body'); } function tab(name, lines) { var elem = $('
    ' + Utils$1.shortText(name, 10) + ' - ' + lines.length + '
    '); elem.on('hover:enter', function () { show$1(name); last_tab = name; }).on('hover:focus', function (e) { scroll_tabs.update($(e.target)); }); scroll_tabs.append(elem); if (!last_tab) last_tab = name; if (last_tab == name) show$1(name); } function build() { html$6 = Template.get('console'); scroll_body = new Scroll({ over: true, mask: true }); scroll_tabs = new Scroll({ horizontal: true }); for (var i in items) { tab(i, items[i]); } html$6.find('.console__tabs').append(HeadBackward(Lang.translate('menu_console'))).append(scroll_tabs.render()); html$6.find('.console__body').append(scroll_body.render()); scroll_body.height(html$6.find('.console__tabs')); $('body').append(html$6); } function add(name, message, message_original) { if (!items[name]) { items[name] = []; original[name] = []; } var where_items = items[name]; var where_original = original[name]; var time = Utils$1.parseTime(Date.now()).time_full; try { Arrays.insert(where_items, 0, '
    ' + time + ' - ' + message + '
    '); Arrays.insert(where_original, 0, { time: Date.now(), message: message_original }); if (Storage.get('terminal_access', '') && Socket.terminalAccess()) { Socket.send('terminal_console', { name: name, time: Date.now(), message: message_original }); } } catch (e) { Arrays.insert(where_items, 0, '
    ' + time + ' - Failed to print line
    '); Arrays.insert(where_original, 0, { time: Date.now(), message: 'Failed to print line' }); } if (where_items.length > 50) where_items.pop(); if (where_original.length > 200) where_original.pop(); } function escapeHtml(text) { return text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } function decode(arr) { if (Arrays.isObject(arr) || Arrays.isArray(arr)) { try { arr = JSON.stringify(arr); } catch (e) { arr = '[noview]'; } } else if (typeof arr === 'string' || typeof arr === 'number' || typeof arr === 'boolean') { arr = escapeHtml(arr + ''); } else { var a = []; for (var i in arr) { a.push(i + ': ' + arr[i]); } arr = JSON.stringify(a); } return arr; } function follow() { var _get_logger_function = function _get_logger_function(func, color) { var prefix = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; return function () { var msgs = []; var mcon = []; var orgn = []; while (arguments.length) { var arr = [].shift.call(arguments); msgs.push(Utils$1.shortText(decode(arr), 600)); mcon.push(arr); orgn.push(decode(arr)); } var name = msgs[0]; if (msgs.length < 2) { name = 'Other'; } else { // Add color and prefix to lampa console var spanColor = color || Utils$1.stringToHslColor(msgs[0], 50, 65); msgs[0] = '' + msgs[0] + ''; // Add brackets to real log if (mcon.length > 0) { mcon[0] = '[' + mcon[0] + ']'; } } if (prefix == 'ERROR' || prefix == 'WARNING') { add(prefix == 'ERROR' ? 'Errors' : 'Warnings', msgs.join(' '), orgn); } add(name, msgs.join(' '), orgn); func.apply(console, mcon); }; }; var called = { log: _get_logger_function(console_orig.log, null), error: _get_logger_function(console_orig.error, 'red', 'ERROR'), warn: _get_logger_function(console_orig.warn, 'yellow', 'WARNING') }; Timer.add(1000, function () { // Заменяем консоль на свою console.log = called.log; console.error = called.error; console.warn = called.warn; }); console.log = called.log; console.error = called.error; console.warn = called.warn; window.addEventListener("error", function (e) { var stack = (e.error && e.error.stack ? e.error.stack : e.stack || '').split("\n").join('
    '); var message = typeof e.error == 'string' ? e.error : (e.error || e).message; var filename = e.filename || (e.error && e.error.fileName ? e.error.fileName : ''); var noty = []; message && noty.push('Message: ' + message); filename && noty.push('In: ' + filename); stack && noty.push('Stack: ' + stack); noty = noty.join('

    '); var welcome = $('.welcome'); if (welcome.length) { welcome.fadeOut(500, function () { Noty.show(noty, { time: 8000 }); }); } add('Errors', noty, noty.replace(/
    /g, '\n')); if (!(stack.indexOf('resetTopStyle') >= 0 || stack.indexOf('Blocked a frame') >= 0 || stack.indexOf('global code@http') >= 0)) Noty.show(noty, { time: 8000 }); }); } var Console = { init: init$r, "export": function _export() { return original; } }; function create$3() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var html = Template.get('files', params.movie); html.addClass('layer--width'); if (params.movie.id) { html.find('.selector').on('hover:enter', function () { if (Activity.all().length > 1) { Activity.back(); } else { Activity.push({ url: params.movie.url, component: 'full', id: params.movie.id, method: params.movie.name ? 'tv' : 'movie', card: params.movie, source: params.movie.source }); } }); } else { html.find('.selector').removeClass('selector'); } this.render = function () { return html; }; this.append = function (add) { html.find('.files__body').append(add); }; this.destroy = function () { html.remove(); html = null; }; this.clear = function () { html.find('.files__body').empty(); }; } function create$2() { var html; this.create = function () { html = Template.get('info'); }; this.update = function (data) { var nofavorite = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var create = ((data.release_date || data.first_air_date || '0000') + '').slice(0, 4); var vote = parseFloat((data.vote_average || 0) + '').toFixed(1); html.find('.info__title').text(data.title); html.find('.info__title-original').text((create == '0000' ? '' : create + ' - ') + (data.original_title || '')); html.find('.info__vote span').text(vote); html.find('.info__vote').toggleClass('hide', !(vote > 0)); html.find('.info__icon').removeClass('active'); if (!nofavorite) { var status = Favorite.check(data); $('.icon--book', html).toggleClass('active', status.book); $('.icon--like', html).toggleClass('active', status.like); $('.icon--wath', html).toggleClass('active', status.wath); } html.find('.info__right').toggleClass('hide', nofavorite); }; this.render = function () { return html; }; this.empty = function () { this.update({ title: Lang.translate('more'), original_title: Lang.translate('more_results'), vote_average: 0 }, true); }; this.destroy = function () { html.remove(); html = null; }; } /** * Карточка * @param {object} data * @param {{isparser:boolean, card_small:boolean, card_category:boolean, card_collection:boolean, card_wide:true}} params */ function Card(data) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; console.warn('Card is deprecated'); this.data = data; this.params = params; Arrays.extend(data, { title: data.name, original_title: data.original_name, release_date: data.first_air_date }); data.release_year = ((data.release_date || data.birthday || '0000') + '').slice(0, 4); function remove(elem) { if (elem) elem.remove(); } /** * Загрузить шаблон */ this.build = function () { this.card = Template.js(params.isparser ? 'card_parser' : 'card', data); this.img = this.card.querySelector('.card__img') || {}; this.card.card_data = data; if (params.isparser) { var elem_title = this.card.querySelector('.card-parser__title'); var elem_size = this.card.querySelector('.card-parser__size'); var elem_details = this.card.querySelector('.card-parser__details'); if (elem_title) elem_title.innerText = data.Title; if (elem_size) elem_size.innerText = data.size; var seeds = document.createElement('div'); var grabs = document.createElement('div'); elem_details.innerHTML = ''; seeds.innerHTML = Lang.translate('torrent_item_seeds') + ': ' + data.Seeders + ''; grabs.innerHTML = Lang.translate('torrent_item_grabs') + ': ' + data.Peers + ''; elem_details.appendChild(seeds); elem_details.appendChild(grabs); } else { var _elem_title = this.card.querySelector('.card__title'); if (_elem_title) _elem_title.innerText = data.title; if (data.original_name) { var type_elem = document.createElement('div'); type_elem.classList.add('card__type'); type_elem.innerText = data.original_name ? 'TV' : 'MOV'; this.card.querySelector('.card__view').appendChild(type_elem); this.card.classList.add(data.original_name ? 'card--tv' : 'card--movie'); } if (params.card_small) { this.card.classList.add('card--small'); remove(this.card.querySelector('.card__title')); remove(this.card.querySelector('.card__age')); } if (params.card_category) { this.card.classList.add('card--category'); } if (params.card_explorer) { this.card.classList.add('card--explorer'); } if (params.card_collection) { this.card.classList.add('card--collection'); remove(this.card.querySelector('.card__age')); } if (params.card_wide) { this.card.classList.add('card--wide'); data.poster = data.cover; if (data.promo || data.promo_title) { var promo_wrap = document.createElement('div'); promo_wrap.classList.add('card__promo'); if (data.promo_title) { var promo_title = document.createElement('div'); promo_title.classList.add('card__promo-title'); promo_title.innerText = data.promo_title; promo_wrap.appendChild(promo_title); } if (data.promo) { var promo_text = document.createElement('div'); promo_text.classList.add('card__promo-text'); promo_text.innerText = data.promo.slice(0, 110) + (data.promo.length > 110 ? '...' : ''); promo_wrap.appendChild(promo_text); } this.card.querySelector('.card__view').appendChild(promo_wrap); } if (Storage.field('light_version')) remove(this.card.querySelector('.card__title')); remove(this.card.querySelector('.card__age')); } if (data.release_year == '0000') { remove(this.card.querySelector('.card__age')); } else { var year = this.card.querySelector('.card__age'); if (year) year.innerText = data.release_year; } var vote = parseFloat((data.cub_hundred_rating || data.vote_average || 0) + '').toFixed(1); if (vote > 0) { var vote_elem = document.createElement('div'); vote_elem.classList.add('card__vote'); vote_elem.innerText = data.cub_hundred_fire ? Utils$1.bigNumberToShort(data.cub_hundred_fire) : vote >= 10 ? 10 : vote; this.card.querySelector('.card__view').appendChild(vote_elem); } var qu = data.quality || data.release_quality; if (qu && Storage.field('card_quality') && !data.original_name) { var quality = document.createElement('div'); quality.classList.add('card__quality'); var quality_inner = document.createElement('div'); quality_inner.innerText = qu; quality.appendChild(quality_inner); this.card.querySelector('.card__view').appendChild(quality); } } this.card.addEventListener('visible', this.visible.bind(this)); this.card.addEventListener('update', this.update.bind(this)); }; /** * Загрузить картинку */ this.image = function () { var _this = this; if (params.isparser) return; this.img.onload = function () { _this.card.classList.add('card--loaded'); }; this.img.onerror = function () { TMDB$1.broken(); console.log('Img', 'noload', _this.img.src); _this.img.src = './img/img_broken.svg'; }; }; /** * Добавить иконку * @param {string} name */ this.addicon = function (name) { var icon = document.createElement('div'); icon.classList.add('card__icon'); icon.classList.add('icon--' + name); this.card.querySelector('.card__icons-inner').appendChild(icon); }; /** * Обносить состояние карточки */ this.update = function () { if (params.isparser) return; this.watched_checked = false; if (this.watched_wrap) remove(this.watched_wrap); this.favorite(); if (this.card.classList.contains('focus')) this.watched(); }; /** * Какие серии просмотрено */ this.watched = function () { var _this2 = this; if (!Storage.field('card_episodes')) return; if (!this.watched_checked) { var get = function get(callback) { if (data.original_name) TimeTable.get(data, callback);else callback([]); }; get(function (episodes) { var viewed; episodes.forEach(function (ep) { var hash = Utils$1.hash([ep.season_number, ep.season_number > 10 ? ':' : '', ep.episode_number, data.original_title].join('')); var view = Timeline$1.view(hash); if (view.percent) viewed = { ep: ep, view: view }; }); if (!viewed && data.original_name) { var last = Storage.get('online_watched_last', '{}'); var filed = last[Utils$1.hash(data.original_title)]; if (filed && filed.episode) { viewed = { ep: { episode_number: filed.episode, name: Lang.translate('full_episode') + ' ' + filed.episode }, view: Timeline$1.view(Utils$1.hash([filed.season, filed.season > 10 ? ':' : '', filed.episode, data.original_title].join(''))) }; } } if (!viewed && !data.original_name) { var time = Timeline$1.view(Utils$1.hash([data.original_title].join(''))); if (time.percent) { viewed = { ep: { name: Lang.translate('title_viewed') + ' ' + (time.time ? Utils$1.secondsToTimeHuman(time.time) : time.percent + '%') }, view: time }; } } if (viewed) { var soon = []; var next = episodes.slice(episodes.indexOf(viewed.ep)).filter(function (ep) { return ep.air_date; }).filter(function (ep) { var date = Utils$1.parseToDate(ep.air_date).getTime(); if (date > Date.now()) soon.push(ep); return date < Date.now(); }).slice(0, 5); if (next.length == 0) next = [viewed.ep]; if (soon.length && next.length < 5 && !next.find(function (n) { return n.episode_number == soon[0].episode_number; })) next.push(soon[0]); var wrap = Template.js('card_watched', {}); next.forEach(function (ep) { var div = document.createElement('div'); var span = document.createElement('span'); var date = Utils$1.parseToDate(ep.air_date); var now = Date.now(); var days = Math.ceil((date.getTime() - now) / (24 * 60 * 60 * 1000)); div.classList.add('card-watched__item'); div.appendChild(span); span.innerText = (ep.episode_number ? ep.episode_number + ' - ' : '') + (days > 0 ? Lang.translate('full_episode_days_left') + ': ' + days : ep.name || Lang.translate('noname')); if (ep == viewed.ep) div.appendChild(Timeline$1.render(viewed.view)[0]); wrap.querySelector('.card-watched__body').appendChild(div); }); _this2.watched_wrap = wrap; var view = _this2.card.querySelector('.card__view'); view.insertBefore(wrap, view.firstChild); } }); this.watched_checked = true; } }; /** * Обновить иконки на закладки */ this.favorite = function () { var status = Favorite.check(data); var marker = this.card.querySelector('.card__marker'); var marks = ['look', 'viewed', 'scheduled', 'continued', 'thrown']; this.card.querySelector('.card__icons-inner').innerHTML = ''; if (status.book) this.addicon('book'); if (status.like) this.addicon('like'); if (status.wath) this.addicon('wath'); if (status.history || Timeline$1.watched(data)) this.addicon('history'); var any_marker = marks.find(function (m) { return status[m]; }); if (any_marker) { if (!marker) { marker = document.createElement('div'); marker.addClass('card__marker'); marker.append(document.createElement('span')); this.card.querySelector('.card__view').append(marker); } marker.find('span').text(Lang.translate('title_' + any_marker)); marker.removeClass(marks.map(function (m) { return 'card__marker--' + m; }).join(' ')).addClass('card__marker--' + any_marker); } else if (marker) marker.remove(); }; /** * Вызвали меню * @param {object} target * @param {object} data */ this.onMenu = function (target, data) { var _this3 = this; var enabled = Controller.enabled().name; var status = Favorite.check(data); var menu_plugins = []; var menu_favorite = [{ title: Lang.translate('title_book'), where: 'book', checkbox: true, checked: status.book }, { title: Lang.translate('title_like'), where: 'like', checkbox: true, checked: status.like }, { title: Lang.translate('title_wath'), where: 'wath', checkbox: true, checked: status.wath }, { title: Lang.translate('menu_history'), where: 'history', checkbox: true, checked: status.history }]; if (window.lampa_settings.account_use) { var marks = ['look', 'viewed', 'scheduled', 'continued', 'thrown']; menu_favorite.push({ title: Lang.translate('settings_cub_status'), separator: true }); marks.forEach(function (m) { menu_favorite.push({ title: Lang.translate('title_' + m), where: m, picked: status[m], collect: true, noenter: !Account$1.hasPremium() }); }); } object$2.plugins.forEach(function (plugin) { if (plugin.type == 'video' && plugin.onContextMenu && plugin.onContextLauch) { menu_plugins.push({ title: plugin.name, subtitle: plugin.subtitle || plugin.description, onSelect: function onSelect() { if (document.body.classList.contains('search--open')) Search.close(); if (!data.imdb_id && data.source == 'tmdb') { Loading$1.start(function () { Loading$1.stop(); Controller.toggle(enabled); }); TMDB.external_imdb_id({ type: data.name ? 'tv' : 'movie', id: data.id }, function (imdb_id) { Loading$1.stop(); data.imdb_id = imdb_id; plugin.onContextLauch(data); }); } else plugin.onContextLauch(data); } }); } }); if (menu_plugins.length) menu_plugins.push({ title: Lang.translate('more'), separator: true }); var menu_main = menu_plugins.length ? menu_plugins.concat(menu_favorite) : menu_favorite; if (this.onMenuShow) this.onMenuShow(menu_main, this.card, data); Select.show({ title: Lang.translate('title_action'), items: menu_main, onBack: function onBack() { Controller.toggle(enabled); }, onCheck: function onCheck(a) { if (params.object) data.source = params.object.source; if (a.where) { Favorite.toggle(a.where, data); _this3.favorite(); } }, onSelect: function onSelect(a) { if (params.object) data.source = params.object.source; if (a.collect) { Favorite.toggle(a.where, data); _this3.favorite(); } if (_this3.onMenuSelect) _this3.onMenuSelect(a, _this3.card, data); Controller.toggle(enabled); }, onDraw: function onDraw(item, elem) { if (elem.collect) { if (!Account$1.hasPremium()) { var wrap = $('
    '); wrap.append(Template.js('icon_lock')); item.find('.selectbox-item__checkbox').remove(); item.append(wrap); item.on('hover:enter', function () { Select.close(); Account$1.showCubPremium(); }); } } } }); }; /** * Создать */ this.create = function () { var _this4 = this; this.build(); this.card.addEventListener('hover:focus', function () { _this4.watched(); if (_this4.onFocus) _this4.onFocus(_this4.card, data); }); this.card.addEventListener('hover:touch', function () { _this4.watched(); if (_this4.onTouch) _this4.onTouch(_this4.card, data); }); this.card.addEventListener('hover:hover', function () { _this4.watched(); if (_this4.onHover) _this4.onHover(_this4.card, data); }); this.card.addEventListener('hover:enter', function () { if (_this4.onEnter) _this4.onEnter(_this4.card, data); }); this.card.addEventListener('hover:long', function () { if (_this4.onMenu) _this4.onMenu(_this4.card, data); }); this.image(); }; /** * Загружать картинку если видна карточка */ this.visible = function () { var src = ''; if (params.card_wide && data.backdrop_path) src = Api.img(data.backdrop_path, 'w780');else if (params.card_collection && data.backdrop_path) src = Api.img(data.backdrop_path, 'w500');else if (data.poster_path) src = Api.img(data.poster_path);else if (data.profile_path) src = Api.img(data.profile_path);else if (data.poster) src = data.poster;else if (data.img) src = data.img;else src = './img/img_broken.svg'; this.img.src = src; this.update(); if (this.onVisible) this.onVisible(this.card, data); }; /** * Уничтожить */ this.destroy = function () { this.img.onerror = function () {}; this.img.onload = function () {}; this.img.src = ''; remove(this.card); this.card = null; this.img = null; }; /** * Рендер * @returns {object} */ this.render = function (js) { return js ? this.card : $(this.card); }; } var html$5 = $("
    \n
    \n
    \n \n \n \n \n \n \n
    \n
    \n
    \n
    "); var body = html$5.find('.helper__text'), time; var memorys = {}; var remember = 1000 * 60 * 60 * 14; function show(name, text, elem) { if (!Storage.field('helper')) return; var help = memorys[name]; if (!help) { help = { time: 0, count: 0 }; if (_typeof(memorys) !== 'object') memorys = {}; //хз, вылазит ошибка, что в переменную true нельзя записать значение, откуда там true хз memorys[name] = help; } if (help.time + remember < Date.now() && help.count < 3) { help.time = Date.now(); help.count++; Storage.set('helper', memorys); clearTimeout(time); time = setTimeout(function () { html$5.removeClass('helper--visible'); }, 10000); body.html(text); html$5.addClass('helper--visible'); if (elem) { var blink = $(''); elem.append(blink); setTimeout(function () { blink.remove(); }, 3000); } } } function init$q() { memorys = Storage.cache('helper', 300, {}); Settings.listener.follow('open', function (e) { if (e.name == 'more') { e.body.find('.helper--start-again').on('hover:enter', function () { memorys = {}; Storage.set('helper', memorys); Noty.show(Lang.translate('helper_cleared')); }); } }); $('body').append(html$5); } var Helper$1 = { show: show, init: init$q }; /* let tizen = { ApplicationControlData: ()=>{}, ApplicationControl: ()=>{}, application:{ launchAppControl: ()=>{} } } */ /** * Запуск */ function init$p() { if (typeof tizen !== 'undefined') { Timer.add(1000 * 60 * 10, lauchPick); lauchPick(); deepLink(); window.addEventListener('appcontrol', deepLink); try { console.log('Tizen', 'current id', tizen.application.getCurrentApplication().appInfo.id); } catch (e) {} } } /** * Установить данные * @param {{sections:[{title:string,position:integer,tiles:[{cardToTile}]}]}} data */ function setPick(data) { var service_id = tizen.application.getCurrentApplication().appInfo.id.split('.')[0] + '.service'; var controll_data = new tizen.ApplicationControlData('caller', ['ForegroundApp', JSON.stringify(data)]); var controll_app = new tizen.ApplicationControl('http://tizen.org/appcontrol/operation/pick', null, 'image/*', null, [controll_data]); tizen.application.launchAppControl(controll_app, service_id, function () { console.log('Tizen', 'service', 'launch success'); }, function (error) { console.log('Tizen', 'service', 'error:', JSON.stringify(error)); }); } /** * Карточку в данные * @param {{title:string, name:string, poster_path:string, release_date:string}} card - карточка * @param {string} subtitle * @returns {{title:string, subtitle:string, image_ratio:string, image_url:string, action_data:string, is_playable:boolean}} */ function cardToTile(card, subtitle) { var relise = ((card.release_date || card.first_air_date || '0000') + '').slice(0, 4); var elem = { title: card.title || card.name, subtitle: subtitle || relise, image_ratio: '1by1', image_url: card.poster ? card.poster : card.img ? card.img : 'http://imagetmdb.com/t/p/w300/' + card.poster_path, action_data: JSON.stringify(card), is_playable: false }; return elem; } /** * Строим данные */ function lauchPick() { var data = { sections: [] }; console.log('Tizen', 'start pick'); var status = new Status(3); status.onComplite = function (result) { if (result.popular) data.sections.push(result.popular); if (result.continues) data.sections.push(result.continues); if (result.notice) data.sections.push(result.notice); console.log('Tizen', 'set sections', data.sections.length); if (data.sections.length) setPick(data); }; Account$1.Api.notices(function (notices) { var new_notices = notices.filter(function (n) { return !n.viewed; }).slice(0, 3); if (new_notices.length) { var section = { title: Lang.translate('title_notice'), tiles: [], position: 0 }; new_notices.forEach(function (noty) { var info = JSON.parse(noty.data); section.tiles.push(cardToTile(info.card, info.type == 'new_episode' ? Lang.translate('notice_new_episode') : Lang.translate('notice_in_quality'))); }); status.append('notice', section); } else status.error(); }); TMDB.get('movie/popular', {}, function (result) { if (result.results.length) { var section = { title: Lang.translate('title_popular_movie'), position: 2, tiles: result.results.slice(0, 10).map(function (c) { return cardToTile(c); }) }; status.append('popular', section); } else status.error(); }, status.error.bind(status), { life: 60 * 24 * 3 }); var continues = Favorite.continues('tv'); if (continues.length) { var section = { title: Lang.translate('title_continue'), position: 1, tiles: continues.slice(0, 7).map(function (c) { return cardToTile(c); }) }; status.append('continues', section); } else status.error(); } /** * Перехват запроса на открытие карточки */ function deepLink() { var requestedAppControl = tizen.application.getCurrentApplication().getRequestedAppControl(); if (requestedAppControl) { var appControlData = requestedAppControl.appControl.data; for (var i = 0; i < appControlData.length; i++) { if (appControlData[i].key == 'PAYLOAD') { var action_data = JSON.parse(appControlData[i].value[0]).values; var json = JSON.parse(action_data); window.start_deep_link = { url: json.url, component: 'full', id: json.id, method: json.name ? 'tv' : 'movie', card: json, source: json.source || 'tmdb' }; if (window.appready) { Activity.push(window.start_deep_link); } console.log('Tizen', 'open deep link', window.start_deep_link); } } } } var Tizen = { init: init$p }; function create$1(data) { var _this = this; var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var content = Template.js('items_line', { title: data.title }); var body = content.querySelector('.items-line__body'); var scroll = new Scroll({ horizontal: true, step: params.card_wide ? 600 : 300 }); var items = []; var active = 0; var tv = Platform.screen('tv'); var view = (tv ? Lampa.Storage.field('interface_size') == 'small' ? 7 : 6 : 12) + (params.align_left ? 4 : 0); var more; var last; console.warn('Component InteractionLine is deprecated.'); var onmore = function onmore() { if (_this.onEnter) _this.onEnter(); if (data.onMore) data.onMore(data);else if (_this.onMore) { _this.onMore(data); } else { Activity.push({ url: data.url, title: data.title || Lang.translate('title_category'), component: 'category_full', page: 1, genres: params.genres, filter: data.filter, source: data.source || params.object.source }); } }; this.event = function (type) { Lampa.Listener.send('line', { line: this, type: type, params: params, data: data, scroll: scroll, body: body, items: items, active: active }); }; this.create = function () { var _this2 = this; this.event('create'); scroll.body(true).addClass('items-cards mapping--line'); content.querySelector('.items-line__title').innerHTML = data.title; content.classList.add('items-line--type-' + (params.type || 'none')); content.addEventListener('visible', this.visible.bind(this)); data.results.slice(0, view).forEach(this.append.bind(this)); body.appendChild(scroll.render(true)); scroll.onWheel = function (step) { if (!Controller.own(_this2)) _this2.toggle(); Controller.enabled().controller[step > 0 ? 'right' : 'left'](); }; scroll.onScroll = this.attach.bind(this); }; /* События this.onAppend = function(){} this.onFocus = function(){} this.onEnter = function(){} this.onSelect = function(){} this.onMore = function(){} this.onFocusMore = function(){} this.onLeft = function(){} this.onBack = function(){} this.onDown = function(){} this.onUp = function(){} */ this.visible = function () { data.results.slice(0, view).forEach(this.append.bind(this)); if ((data.results.length >= 20 || data.more) && !params.nomore) { var button = document.createElement('div'); button.classList.add('items-line__more'); button.classList.add('selector'); button.innerText = Lang.translate('more'); button.addEventListener('hover:enter', onmore); content.querySelector('.items-line__head').appendChild(button); } this.event('visible'); Layer.visible(scroll.render(true)); }; this.append = function (element) { var _this3 = this; if (element.ready) return; element.ready = true; var card = params.cardClass ? params.cardClass(element, params) : element.cardClass ? element.cardClass(element, params) : new Card(element, params); card.create(); card.onFocus = function (target, card_data) { last = target; var prev_active = active; active = items.indexOf(card); if (active > 0 || prev_active > active) scroll.update(items[active].render(true), params.align_left ? false : true); if (!data.noimage) Background.change(Utils$1.cardImgBackground(card_data)); if (_this3.onFocus) _this3.onFocus(card_data); }; card.onEnter = function (target, card_data) { last = target; if (_this3.onEnter) _this3.onEnter(target, card_data); if (_this3.onSelect) return _this3.onSelect(target, card_data); if (!card_data.source) card_data.source = params.object.source; if (typeof card_data.gender !== 'undefined') { Activity.push({ url: card_data.url, title: Lang.translate('title_person'), component: 'actor', id: card_data.id, source: card_data.source || params.object.source }); } else { Activity.push({ url: card_data.url, component: 'full', id: card_data.id, method: card_data.name ? 'tv' : 'movie', card: card_data, source: card_data.source || params.object.source }); } }; card.onHover = function (target, card_data) { if (_this3.onHover) _this3.onHover(card_data); }; card.onVisible = function () { if (Controller.own(_this3)) Controller.collectionAppend(card.render(true)); }; if (this.onMenu) card.onMenu = this.onMenu; if (params.card_events) { for (var i in params.card_events) { card[i] = params.card_events[i]; } } scroll.append(card.render(true)); items.push(card); if (this.onAppend) this.onAppend(card); this.event('append'); return card.render(true); }; this.more = function () { var _this4 = this; more = new More$1(params); more.create(); more.render(true).on('hover:focus', function (target) { last = target; active = items.indexOf(more); scroll.update(more.render(true), params.align_left ? false : true); if (_this4.onFocusMore) _this4.onFocusMore(); }); more.render(true).on('hover:enter', onmore.bind(this)); scroll.append(more.render(true)); more.size(items[items.length - 1].render(true)); items.push(more); return more.render(true); }; this.attach = function () { var size = tv ? (Math.round(active / view) + 1) * view + 1 : data.results.length; data.results.slice(0, size).filter(function (e) { return !e.ready; }).forEach(this.append.bind(this)); if (!more && !params.nomore && data.results.length == data.results.filter(function (e) { return e.ready; }).length && data.results.length >= 20) { var more_item = this.more(); if (Controller.own(this)) Controller.collectionAppend(more_item); } Layer.visible(scroll.render(true)); }; this.toggle = function () { var _this5 = this; Controller.add('items_line', { link: this, toggle: function toggle() { Controller.collectionSet(scroll.render(true)); Controller.collectionFocus(items.length ? last : false, scroll.render(true)); if (_this5.onToggle) _this5.onToggle(_this5); _this5.event('toggle'); }, update: function update() {}, right: function right() { Navigator.move('right'); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else if (_this5.onLeft) _this5.onLeft();else Controller.toggle('menu'); }, down: this.onDown, up: this.onUp, gone: function gone() {}, back: this.onBack }); Controller.toggle('items_line'); }; this.render = function (js) { return js ? content : $(content); }; this.destroy = function () { Arrays.destroy(items); scroll.destroy(); content.remove(); items = null; this.event('destroy'); }; } function component$3(object) { var scroll = new Scroll({ mask: true, over: true, scroll_by_item: true, end_ratio: 1.5 }); var items = []; var html = document.createElement('div'); var active = 0; console.warn('Component InteractionMain is deprecated.'); this.create = function () {}; this.empty = function () { var button; if (object.source == 'tmdb') { button = $(''); button.find('.selector').on('hover:enter', function () { Storage.set('source', 'cub'); Activity.replace({ source: 'cub' }); }); } var empty = new Empty(); if (button) empty.append(button); empty.addInfoButton(); html.appendChild(empty.render(true)); this.start = empty.start.bind(empty); this.activity.loader(false); this.activity.toggle(); }; this.loadNext = function () { var _this = this; if (this.next && !this.next_wait && items.length) { this.next_wait = true; this.next(function (new_data) { _this.next_wait = false; if (!items.length) return; new_data.forEach(_this.append.bind(_this)); Layer.visible(items[active + 1].render(true)); }, function () { _this.next_wait = false; }); } }; this.build = function (data) { var _this2 = this; scroll.minus(); scroll.onWheel = function (step) { if (!Controller.own(_this2)) _this2.start(); if (step > 0) _this2.down();else if (active > 0) _this2.up(); }; scroll.onEnd = this.loadNext.bind(this); if (this.onLinesBuild) this.onLinesBuild(data); data.forEach(this.append.bind(this)); html.appendChild(scroll.render(true)); Layer.update(html); this.activity.loader(false); this.activity.toggle(); Layer.visible(html); }; this.append = function (element) { if (element.ready) return; element.ready = true; var item = new create$1(element, { url: element.url, genres: object.genres, object: object, card_wide: element.wide, card_small: element.small, card_broad: element.broad, card_collection: element.collection, card_category: element.category, card_events: element.card_events, cardClass: element.cardClass, nomore: element.nomore, type: element.line_type || 'cards' }); item.create(); this.push(item, element); }; this.back = function () { Activity.backward(); }; this.push = function (item, element) { item.onDown = this.down.bind(this); item.onUp = this.up.bind(this); item.onBack = this.back.bind(this); if (this.onMore) item.onMore = this.onMore.bind(this); items.push(item); if (this.onAppend) this.onAppend(item, element); scroll.append(item.render(true)); }; this.down = function () { active++; active = Math.min(active, items.length - 1); scroll.update(items[active].render(true)); items[active].toggle(); }; this.up = function () { active--; if (active < 0) { active = 0; Controller.toggle('head'); } else { items[active].toggle(); scroll.update(items[active].render(true)); } }; this.start = function () { var _this3 = this; Controller.add('content', { link: this, toggle: function toggle() { if (_this3.activity.canRefresh()) return false; if (items.length) items[active].toggle(); }, update: function update() {}, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else Controller.toggle('menu'); }, right: function right() { Navigator.move('right'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('head'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down'); }, back: this.back }); Controller.toggle('content'); }; this.refresh = function () { this.activity.needRefresh(); }; this.pause = function () {}; this.stop = function () {}; this.render = function (js) { return js ? html : $(html); }; this.destroy = function () { Arrays.destroy(items); scroll.destroy(); html.remove(); items = []; if (this.onDestroy) this.onDestroy(); }; } function component$2(object) { var network = new Request(); var scroll = new Scroll({ mask: true, over: true, step: 250, end_ratio: 2 }); var items = []; var html = document.createElement('div'); var body = document.createElement('div'); var total_pages = 0; var last; var waitload; var active = 0; console.warn('Component InteractionCategory is deprecated.'); this.create = function () {}; this.empty = function () { var button; if (object.source == 'tmdb') { button = $(''); button.find('.selector').on('hover:enter', function () { Storage.set('source', 'cub'); Activity.replace({ source: 'cub' }); }); } var empty = new Empty(); if (button) empty.append(button); empty.addInfoButton(); html.appendChild(empty.render(true)); this.start = empty.start.bind(empty); this.activity.loader(false); this.activity.toggle(); }; this.next = function () { var _this = this; if (waitload) return; if (object.page < total_pages) { waitload = true; object.page++; this.nextPageReuest(object, function (result) { _this.append(result, true); waitload = false; _this.limit(); }, function () { waitload = false; }); } }; this.nextPageReuest = function (object, resolve, reject) { Api.list(object, resolve.bind(this), reject.bind(this)); }; this.append = function (data, append) { var _this2 = this; data.results.forEach(function (element) { var params = { object: object, card_category: typeof data.card_category == 'undefined' ? true : data.category, card_wide: data.wide, card_small: data.small, card_broad: data.broad, card_collection: data.collection, card_events: data.card_events }; var card = data.cardClass ? data.cardClass(element, params) : element.cardClass ? element.cardClass(element, params) : new Card(element, params); card.create(); card.onFocus = function (target, card_data) { last = target; active = items.indexOf(card); scroll.update(card.render(true)); Background.change(Utils$1.cardImgBackground(card_data)); }; card.onTouch = function (target, card_data) { last = target; active = items.indexOf(card); }; card.onEnter = function (target, card_data) { last = target; if (typeof card_data.gender !== 'undefined') { Activity.push({ url: element.url, title: Lang.translate('title_person'), component: 'actor', id: element.id, source: element.source || object.source }); } else { Activity.push({ url: card_data.url, component: 'full', id: element.id, method: card_data.name ? 'tv' : 'movie', card: element, source: element.source || object.source }); } }; body.appendChild(card.render(true)); items.push(card); if (_this2.cardRender) _this2.cardRender(object, element, card); if (append) Controller.collectionAppend(card.render(true)); }); }; this.limit = function () { var limit_view = 12; var lilit_collection = 36; var colection = items.slice(Math.max(0, active - limit_view), active + limit_view); items.forEach(function (item) { if (colection.indexOf(item) == -1) { item.render(true).classList.remove('layer--render'); } else { item.render(true).classList.add('layer--render'); } }); Navigator.setCollection(items.slice(Math.max(0, active - lilit_collection), active + lilit_collection).map(function (c) { return c.render(true); })); Navigator.focused(last); Layer.visible(scroll.render(true)); }; this.build = function (data) { var _this3 = this; if (data.results.length) { total_pages = data.total_pages; body.addClass('category-full'); scroll.minus(); scroll.onEnd = this.next.bind(this); scroll.onScroll = this.limit.bind(this); scroll.onWheel = function (step) { if (!Controller.own(_this3)) _this3.start(); if (step > 0) Navigator.move('down');else Navigator.move('up'); }; this.append(data); scroll.append(body); html.appendChild(scroll.render(true)); this.limit(); this.activity.loader(false); this.activity.toggle(); } else { this.empty(); } }; this.start = function () { var _this4 = this; Controller.add('content', { link: this, toggle: function toggle() { if (_this4.activity.canRefresh()) return false; Controller.collectionSet(scroll.render(true)); Controller.collectionFocus(last || false, scroll.render(true)); }, left: function left() { if (Navigator.canmove('left')) Navigator.move('left');else Controller.toggle('menu'); }, right: function right() { if (_this4.onRight) { if (Navigator.canmove('right')) Navigator.move('right');else _this4.onRight(); } else Navigator.move('right'); }, up: function up() { if (Navigator.canmove('up')) Navigator.move('up');else Controller.toggle('head'); }, down: function down() { if (Navigator.canmove('down')) Navigator.move('down'); }, back: function back() { Activity.backward(); } }); Controller.toggle('content'); }; this.refresh = function () { this.activity.needRefresh(); }; this.pause = function () {}; this.stop = function () {}; this.render = function (js) { return js ? html : $(html); }; this.destroy = function () { network.clear(); Arrays.destroy(items); scroll.destroy(); html.remove(); body.remove(); items = []; }; } /** * Открывает окно выбора языка на начальном экране * @param {function} callSelected - вызывается при выборе языка, получает код языка * @param {function} callCancel - вызывается при отмене выбора * @returns {void} */ function open$2(callSelected, callCancel) { var html = Template.get('lang_choice', {}); var scroll = new Scroll({ mask: true, over: true }); var codes = Lang.codes(); function selector(code) { var item = $('
    ' + codes[code] + '
    '); item.on('hover:enter click', function (e) { if (callSelected) callSelected(code); html.fadeOut(300, function () { scroll.destroy(); html.remove(); scroll = null; html = null; }); }).on('hover:focus', function (e) { scroll.update($(e.target), true); $('.lang__selector-item', html).removeClass('last-focus'); $(e.target).addClass('last-focus'); html.find('.lang__title').text(Lang.translate('lang_choice_title', code)); html.find('.lang__subtitle').text(Lang.translate('lang_choice_subtitle', code)); }); scroll.append(item); } for (var code in codes) { selector(code); } html.find('.lang__selector').append(scroll.render()); $('body').append(html); Controller.add('language', { toggle: function toggle() { var focus = html.find('[data-code="' + Storage.get('language', 'ru') + '"]'); Controller.collectionSet(scroll.render()); Controller.collectionFocus(focus[0], scroll.render()); }, up: function up() { Navigator.move('up'); }, down: function down() { Navigator.move('down'); }, back: function back() { if (callCancel) { scroll.destroy(); html.remove(); scroll = null; html = null; callCancel(); } } }); Controller.toggle('language'); } var LangChoice = { open: open$2 }; var player; var html$4; var timer; /** * Создать плеер YouTube * @param {string} id - ID видео на YouTube * @return {void} */ function create(id) { html$4 = $('
    '); $('body').append(html$4); player = new YT.Player('youtube-player', { height: window.innerHeight, width: window.innerWidth, playerVars: { 'controls': 0, 'showinfo': 0, 'autohide': 1, 'modestbranding': 1, 'autoplay': 1, 'suggestedQuality': 'hd1080', 'setPlaybackQuality': 'hd1080' }, videoId: id, events: { onReady: function onReady(event) { event.target.setPlaybackQuality('hd1080'); event.target.playVideo(); update$2(); }, onStateChange: function onStateChange(state) { if (state.data == 0) { Controller.toggle('content'); } if (state.data == YT.PlayerState.BUFFERING) { state.target.setPlaybackQuality('hd1080'); } }, onPlaybackQualityChange: function onPlaybackQualityChange(state) { console.log('YouTube', 'quality', state.target.getPlaybackQuality()); } } }); } function update$2() { timer = setTimeout(function () { var progress = player.getCurrentTime() / player.getDuration() * 100; $('#youtube-player__progress').css('width', progress + '%'); Screensaver$2.resetTimer(); update$2(); }, 400); } /** * Начать воспроизведение видео с YouTube * @param {string} id - ID видео на YouTube (например, dQw4w9WgXcQ) * @returns {void} */ function play(id) { if (typeof YT == 'undefined') return; create(id); Controller.add('youtube', { invisible: true, toggle: function toggle() {}, right: function right() { player.seekTo(player.getCurrentTime() + 10, true); }, left: function left() { player.seekTo(player.getCurrentTime() - 10, true); }, enter: function enter() {}, gone: function gone() { destroy(); }, back: function back() { Controller.toggle('content'); } }); Controller.toggle('youtube'); } function destroy() { clearTimeout(timer); player.destroy(); html$4.remove(); html$4 = null; } var YouTube = { play: play }; /** * LG webOS добавление/удаление приложения из лаунчера, но только для виджетов ручной установки */ function init$o() { if (!Platform.is('webos')) return; var field = $("
    \n
    \n \n \n \n \n \n \n
    \n
    ".concat(Lang.translate('settings_webos_launcher'), "
    \n
    ")); Settings.main().render().find('[data-component="more"]').after(field); Settings.main().update(); Lampa.Template.add('settings_webos_launcher', "
    \n
    \n
    #{settings_webos_launcher_add_device}
    \n
    \n
    \n
    #{settings_webos_launcher_remove_device}
    \n
    \n
    "); Settings.listener.follow('open', function (e) { var appid = webOS.fetchAppId(); console.log('WebOS', 'current appid:', appid); if (appid.length == 0) appid = window.lampa_settings.iptv ? 'icva' : 'com.lampa.tv'; if (e.name == 'webos_launcher') { e.body.find('[data-name="add"]').unbind('hover:enter').on('hover:enter', function () { webOS.service.request("luna://com.webos.service.eim", { method: "addDevice", parameters: { "appId": appid, "pigImage": "/pigImage.jpg", "type": "MVPD_IP", "showPopup": true, "label": "Lampa", "description": "Lampa app for LG webOS" }, onSuccess: function onSuccess(res) { Noty.show(Lang.translate('settings_added')); }, onFailure: function onFailure(res) { Noty.show(Lang.translate('title_error') + ': ' + res.errorText); } }); }); e.body.find('[data-name="remove"]').unbind('hover:enter').on('hover:enter', function () { webOS.service.request("luna://com.webos.service.eim", { method: "deleteDevice", parameters: { "appId": appid, "showPopup": true }, onSuccess: function onSuccess(res) { Noty.show(Lang.translate('settings_added')); }, onFailure: function onFailure(res) { Noty.show(Lang.translate('title_error') + ': ' + res.errorText); } }); }); } }); } var WebOSLauncher = { init: init$o }; /** * События через сокет * @returns {void} */ function Event() { var ids = {}; var evokes = {}; function callback(data) { if (data.method == 'callback' && ids[data.callback_name] == data.callback_id) { evokes[data.callback_id](data); evokes[data.callback_id] = function () {}; } } this.call = function (method, params, call) { if (!ids[method]) ids[method] = Utils$1.uid(10); params.callback_id = ids[method]; params.callback_name = method; evokes[params.callback_id] = call; Socket.send('callback', params); }; this.cancel = function (method) { if (ids[method]) { evokes[ids[method]] = function () {}; } }; this.destroy = function () { Socket.listener.remove('message', callback); for (var i in evokes) { evokes[i] = function () {}; } }; Socket.listener.follow('message', callback); } /** * Режим разработчика * @param {function} callSelected Функция, которая вызывается при закрытии меню разработчика */ function open$1(callSelected) { var html = Template.get('lang_choice', {}); var scroll = new Scroll({ mask: true, over: true }); function btn(name, select) { var item = $('
    ' + name + '
    '); item.on('hover:enter', function (e) { if (select) select(); }).on('hover:focus', function (e) { scroll.update($(e.target), true); }).on('click', function (e) { if (select && DeviceInput.canClick(e.originalEvent)) select(); }); scroll.append(item); } function destroy() { html.fadeOut(300, function () { scroll.destroy(); html.remove(); scroll = null; html = null; }); Controller.add('developer', { toggle: function toggle() {} }); } function close() { destroy(); callSelected(); } var btns = [{ name: Lang.translate('title_continue_two'), select: close }, { name: Lang.translate('plugins_remove'), select: function select() { Storage.set('plugins', []); close(); } }, { name: Lang.translate('settings_reset'), select: function select() { localStorage.clear(); window.location.reload(); destroy(); } }]; btns.forEach(function (item) { btn(item.name, item.select); }); html.find('.lang__selector').append(scroll.render()); $('body').append(html); Controller.add('developer', { toggle: function toggle() { Controller.collectionSet(scroll.render()); Controller.collectionFocus(false, scroll.render()); }, up: function up() { Navigator.move('up'); }, down: function down() { Navigator.move('down'); } }); Controller.toggle('developer'); } var Developer = { open: open$1 }; var network; function init$n() { network = new Request(); Socket.listener.follow('message', function (e) { if (e.method == 'premiere') update$1(e.data); }); } function update$1(data) { var id = data.type + '/' + data.id; console.log('Premiere', 'load:', data, 'exist:', Boolean(Notice$1.classes.lampa.notices.find(function (n) { return n.id == id; }))); if (Notice$1.classes.lampa.notices.find(function (n) { return n.id == id; })) return; var codes = Arrays.getKeys(Lang.codes()); network.silent(TMDB$1.api(id + '?append_to_response=translations,credits&language=' + Storage.get('language', 'ru') + '&api_key=' + TMDB$1.key()), function (movie) { network.silent(TMDB$1.api(id + '/images?include_image_language=' + codes.join(',') + '&language=' + Storage.get('language', 'ru') + '&api_key=' + TMDB$1.key()), function (images) { var card = Arrays.clone(movie); console.log('Premiere', 'card loaded', card); delete card.translations; delete card.credits; delete card.credits; delete card.spoken_languages; delete card.production_companies; delete card.production_countries; var notice = { id: id, from: 'cub', title: {}, text: {}, time: Date.now(), poster: {}, card: card }; var translations = movie && movie.translations && Array.isArray(movie.translations.translations) ? movie.translations.translations : []; if (translations.length) { translations.filter(function (t) { return codes.indexOf(t.iso_639_1) >= 0; }).forEach(function (t) { notice.title[t.iso_639_1] = Lang.translate('premiere_title') + ': ' + (t.data.title || t.data.name || movie.title || movie.name); notice.text[t.iso_639_1] = (t.data.overview || movie.overview || '').slice(0, 130) + '...'; }); } else { codes.forEach(function (code) { notice.title[code] = Lang.translate('premiere_title') + ': ' + (movie.title || movie.name || ''); notice.text[code] = (movie.overview || '').slice(0, 130) + '...'; }); } var posters = images && Array.isArray(images.posters) ? images.posters : []; posters.forEach(function (i) { notice.poster[i.iso_639_1] = i.file_path; }); if (movie.credits && movie.credits.cast) { var casts = movie.credits.cast.filter(function (c) { return (c.known_for_department || '').toLowerCase() == 'acting'; }); if (casts.length) { notice.author = {}; codes.forEach(function (c) { notice.author[c] = { name: casts[0].name || casts[0].character, img: casts[0].profile_path, text: Lang.translate('premiere_author_recomend_' + (Math.floor(Math.random() * 5) + 1)) }; }); } } Notice$1.pushNotice('lampa', notice, function () { console.log('Premiere', 'card added'); }, function (er) { console.log('Premiere', 'card added error:', er); }); }); }); } var Premiere = { init: init$n }; /** * Показать уведомление о продлении премиум-статуса * @returns {void} */ function init$m() { if (Account$1.Permit.access && Lang.selected(['ru', 'uk', 'be', 'bg']) && window.lampa_settings.account_use) { if (Account$1.Permit.user.premium && !Account$1.hasPremium()) setTimeout(push$2, 5000); } } function push$2() { var now = new Date(); var id = 'extend_premium_' + now.getFullYear() + '_' + now.getMonth(); if (Notice$1.classes.lampa.notices.find(function (n) { return n.id == id; })) return; var notice = { id: id, from: 'cub', title: { ru: 'Продлите CUB Premium прямо сейчас!', uk: 'Продовжіть CUB Premium прямо зараз!', be: 'Працягнеце CUB Premium прама зараз!', bg: 'Подновете CUB Premium точно сега!' }, text: { ru: 'Ваша подписка на CUB Premium истекла! Не упустите шанс продлить доступ к эксклюзивному контенту и дополнительным функциям. Обновите премиум-статус сейчас и наслаждайтесь всеми преимуществами CUB без ограничений!', uk: 'Ваша передплата на CUB Premium закінчилася! Не пропустіть шанс продовжити доступ до ексклюзивного контенту та додаткових функцій. Оновіть преміум-статус зараз та насолоджуйтесь усіма перевагами CUB без обмежень!', be: 'Ваша падпіска на CUB Premium скончылася! Не выпусціце шанец падоўжыць доступ да эксклюзіўнага кантэнту і дадатковым функцый. Абнавіце прэміум-статус зараз і атрымлівайце асалоду ад усімі перавагамі CUB без абмежаванняў!', bg: 'Вашият абонамент за CUB Premium е изтекъл! Не пропускайте шанса си да подновите достъпа си до ексклузивно съдържание и допълнителни функции. Надстройте до премиум статус сега и се насладете на всички предимства на CUB без ограничения!' }, time: Date.now(), icon: Utils$1.protocol() + object$2.cub_domain + '/img/icons/premium_two.svg' }; Notice$1.pushNotice('lampa', notice, function () {}, function (er) {}); } var Extend = { init: init$m }; var Manager = new VastManager({ api: 'banner', cooling: 1000 * 60 * 20 }); var adContainer, adDisplayContainer, adsLoader; var adsManager = null; var showTimeout = null; var timeout = 1000 * 60; var banner = null; function init$l() { Manager.init(); Lampa.Player.listener.follow('ready', function () { Manager.markCooling(); }); Lampa.Player.listener.follow('destroy', stop); Lampa.PlayerPanel.listener.follow('visible', function (e) { return resize(e.status ? Lampa.PlayerPanel.render()[0].offsetHeight : 0); }); Lampa.PlayerFooter.listener.follow('open', function (e) { return resize(Lampa.PlayerFooter.render().offsetHeight); }); Lampa.PlayerFooter.listener.follow('close', function (e) { return resize(Lampa.PlayerPanel.render()[0].offsetHeight); }); var first = true; Timer.add(1000 * 60, function () { Manager.params.cooling = 1000 * 60 * (window.lampa_settings.developer.enabled ? 2 : 20); var play_data = Lampa.Player.playdata(); var can_show = IMA.canShow(play_data); var vast_banner = play_data.vast_banner && !(Account$1.hasPremium() || Personal.confirm()); if (vast_banner) { vast_banner = { url: play_data.vast_banner, name: 'plugin' }; } if (Lampa.Player.opened() && Manager.coolingReady() && (can_show || vast_banner)) { banner = can_show ? Manager.get(play_data, first) : vast_banner; console.log('Ad', 'show banner', banner); if (banner) { first = false; Manager.markCooling(); stop(); start$3(); } else { first = true; } } }); } function stat(method) { IMA.metric('banner', method, banner.name); } function resize(panelHeight) { if (!adContainer) return; adContainer.style.height = window.innerHeight - panelHeight - (panelHeight ? 20 : 0) + 'px'; if (adsManager) { var w = adContainer.offsetWidth || window.innerWidth; var h = adContainer.offsetHeight || window.innerHeight; try { adsManager.resize(w, h, google.ima.ViewMode.NORMAL); } catch (ex) {} } } function loaded(event) { var video = Lampa.PlayerVideo.video(); if (!Lampa.Player.opened()) return; stat('run'); var adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.uiElements = []; adsManager = event.getAdsManager(video, adsRenderingSettings); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, function (e) { console.log('Ad', 'banner started'); stat('started'); showTimeout = setTimeout(function () { console.log('Ad', 'banner complete'); stat('complete'); stop(); }, timeout); }); adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, function (e) { console.error('Ad', 'manager error', e.getError()); error(200); stop(); }); var player = Lampa.Player.render()[0]; var w = player ? player.offsetWidth : window.innerWidth; var h = player ? player.offsetHeight : window.innerHeight; try { adsManager.init(w, h, google.ima.ViewMode.NORMAL); adsManager.start(); resize(Lampa.Player.render().hasClass('player--panel-visible')); } catch (e) { console.error('Ad', 'init error', e); error(300); stop(); } } function start$3() { IMA.loadSDK3().then(function () { if (!Lampa.Player.opened()) return; var video = Lampa.PlayerVideo.video(); // Контейнер должен быть внутри .player, чтобы IMA SDK // правильно рассчитывал позицию overlay относительно видео var player = Lampa.Player.render()[0]; adContainer = document.createElement('div'); adContainer.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;pointer-events:none;'; player.appendChild(adContainer); adDisplayContainer = new google.ima.AdDisplayContainer(adContainer, video); adsLoader = new google.ima.AdsLoader(adDisplayContainer); adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, loaded); adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, function (e) { console.error('Ad', 'banner loader error', e.getError()); error(100); stop(); }); // initialize() должен вызываться до requestAds() и из пользовательского контекста adDisplayContainer.initialize(); var slotWidth = player.offsetWidth || window.innerWidth; var slotHeight = player.offsetHeight || window.innerHeight; var request = new google.ima.AdsRequest(); request.adTagUrl = IMA.buildUrl(banner.url); request.linearAdSlotWidth = slotWidth; request.linearAdSlotHeight = slotHeight; request.nonLinearAdSlotWidth = slotWidth; request.nonLinearAdSlotHeight = Math.round(slotHeight * 0.3); adsLoader.requestAds(request); stat('launch'); })["catch"](function (e) { console.error('Ad', 'banner SDK load failed', e); }); } function error(code) { IMA.metric('banner', 'error', banner.name); IMA.metric('banner', 'error_' + code, banner.name); } function stop() { clearTimeout(showTimeout); showTimeout = null; if (adsManager) { try { adsManager.stop(); adsManager.destroy(); } catch (e) {} adsManager = null; } if (adsLoader) { try { adsLoader.destroy(); } catch (e) {} adsLoader = null; } if (adContainer) { adContainer.remove(); adContainer = null; } } var Banner = { init: init$l }; function init$k() { Premiere.init(); Extend.init(); if (!Platform.is(['orsay', 'netcast'])) { Preroll.init(); Banner.init(); } } var AdManager = { init: init$k }; /** * Инициализирует навигационную панель (только для мобильных устройств) * @returns {void} */ function init$j() { if (Platform.screen('mobile')) { var bar = Template.get('navigation_bar', {}); bar.find('.navigation-bar__item').on('click', function () { var action = $(this).data('action'); if (action == 'back') Controller.back();else if (action == 'main') { Activity.push({ url: '', title: Lang.translate('title_main') + ' - ' + Storage.field('source').toUpperCase(), component: 'main', source: Storage.field('source') }); } else if (action == 'search') { Lampa.Search.open(); } else if (action == 'settings') { Controller.toggle('settings'); } }); $('body').append(bar); } } var NavigationBar = { init: init$j }; function Endless(onRender) { var _this = this; var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var wrap = document.createElement('div'); var position = params.position || 0; wrap.addClass('endless'); wrap.addEventListener('mousewheel', function (e) { _this.move(e.wheelDelta / 120 > 0 ? -1 : 1); }); var touch; var touchStart = function touchStart(e) { var point = e.touches[0] || e.changedTouches[0]; touch = { position: position, from: point.clientY, to: 0 }; window.addEventListener('touchend', touchEnd); window.addEventListener('touchmove', touchMove); }; var touchMove = function touchMove(e) { var point = e.touches[0] || e.changedTouches[0]; if (touch) { var to = Math.round((point.clientY - touch.from) / (window.innerHeight * 0.1)); if (touch.to !== to) { var move = touch.position - to; touchEnd(); _this.to(move); } } }; var touchEnd = function touchEnd(e) { window.removeEventListener('touchend', touchEnd); window.removeEventListener('touchmove', touchMove); touch = false; }; wrap.addEventListener('touchstart', touchStart); this.move = function (dir) { var dif = position - (position + dir); position += dir; this.draw(dif); }; this.to = function (to) { var dif = position - to; position = to; this.draw(dif); }; this.draw = function (dif) { var render = onRender(position); if (render) { wrap.removeClass('endless-up endless-down'); wrap.style.animation = 'none'; wrap.offsetHeight; wrap.style.animation = null; wrap.empty().append(render); wrap.addClass(dif == -1 ? 'endless-down' : 'endless-up'); } }; this.render = function () { return wrap; }; this.destroy = function () { wrap.remove(); }; this.draw(0); } /** * Скрыть ненужные элементы интерфейса */ function hide() { Settings.listener.follow('open', function (e) { if (e.name == 'main') { e.body.find(['player', 'account', 'parser', 'server', 'plugins'].map(function (a) { return '[data-component="' + a + '"]'; }).join(', ')).addClass('hide'); } else { e.body.find(['start_page', 'card_quality', 'card_episodes', 'proxy_tmdb_auto', 'proxy_tmdb', 'tmdb_proxy_api', 'tmdb_proxy_image', 'card_interfice_type', 'source'].map(function (a) { return '[data-name="' + a + '"]'; }).join(', ')).addClass('hide'); } }); var head = $('.head'); var menu = $('.menu'); $('.open--broadcast, .open--search,.open--notice,.open--premium', head).remove(); $(['catalog', 'feed', 'filter', 'relise', 'anime', 'favorite', 'subscribes', 'timetable', 'mytorrents', 'console', 'about'].map(function (a) { return '[data-action="' + a + '"]'; }).join(', '), menu).remove(); Arrays.remove(TMDB.genres.movie, TMDB.genres.movie.find(function (g) { return g.id == 99; })); var genres_id = [99, 10764, 10766, 10767, 10768, 10763]; genres_id.forEach(function (id) { Arrays.remove(TMDB.genres.tv, TMDB.genres.tv.find(function (g) { return g.id == id; })); }); Lampa.Listener.follow('full', function (e) { if (e.type == 'complite') { e.object.activity.render().find('.full-start-new__buttons, .full-start__icons, .full-start__footer').remove(); } }); } /** * Инициализация демо режима * @returns {void} */ function init$i() { if (window.lampa_settings.demo) { Lampa.Listener.follow('app', function (e) { if (e.type == 'ready') hide(); }); } } var Demo = { init: init$i }; var html$3; var tout; var xmlHTTP; var controll$1; var active$1 = {}; var graph = []; /** * Инициализация теста скорости * @returns {void} */ function init$h() { Settings.listener.follow('open', function (e) { if (e.name == 'server') { var btn = $("
    \n
    ".concat(Lang.translate('speedtest_button'), "
    \n
    ")); btn.on('hover:enter', function () { if (Torserver.ip()) { var params = { url: Torserver.url() + '/download/300' }; if (Storage.field('torrserver_auth')) { params.login = Storage.get('torrserver_login'); params.password = Storage.value('torrserver_password'); } start$2(params); } }); $('[data-name="torrserver_url_two"]', e.body).after(btn); } }); } /** * Запуск теста скорости * @param {object} params - параметры теста * @param {string} params.url - URL для теста скорости * @param {string} [params.login] - логин для авторизации * @param {string} [params.password] - пароль для авторизации * @param {function} [params.onEnd] - вызывается по окончании теста * @param {function} [params.onBack] - вызывается при выходе из теста * @returns {void} */ function start$2(params) { if (html$3) html$3.remove(); active$1 = params; controll$1 = Controller.enabled().name; html$3 = Template.js('speedtest'); html$3.append(HeadBackward('', true)); document.body.append(html$3); var textpath = Array.from(html$3.querySelectorAll('textpath')); textpath.forEach(function (element) { element.html(element.getAttribute('data-text')); }); html$3.find('#speedtest_num-text').html('Mbps'); toggle(); if (active$1.url) testUrl(active$1.url); } function speed2deg(v) { v = parseFloat(v); return v >= 1000 ? 200 : v < 20 ? v * 4 : v < 30 ? (v - 20) * 2 + 80 : v < 60 ? (v - 30) / 1.5 + 100 : v < 100 ? (v - 60) / 2 + 120 : v < 200 ? (v - 100) / 5 + 140 : v < 500 ? (v - 200) / 15 + 160 : (v - 500) / 25 + 180; } function hslToRgb(hue, sat, light) { hue = hue % 360, hue += hue < 0 ? hue += 360 : 0, sat /= 100, light /= 100; function f(n) { var k = (n + hue / 30) % 12; var a = sat * Math.min(light, 1 - light); return parseInt((light - a * Math.max(-1, Math.min(k - 3, 9 - k, 1))) * 255); } return "#" + ((1 << 24) + (f(0) << 16) + (f(8) << 8) + f(4)).toString(16).slice(1); } function setSpeed(v) { v = parseFloat(v); html$3.find('#speedtest_num').innerHTML = v < 1 ? v.toFixed(3) : v < 10 ? v.toFixed(2) : v < 100 ? v.toFixed(1) : Math.round(v); var r = speed2deg(v); var b = html$3.find('#speedtest_progress'); var l = 1256.8; b.style['stroke-dasharray'] = l * r / 360 + ',' + l; b.style.stroke = hslToRgb(330 + r, 80, 45); html$3.find('#speedtest_graph').setAttribute('points', graph.map(function (pt) { return pt.join(','); }).join(' ')); } function normalizeUrl(base, link) { if (link[0] === '/') return base.replace(/^(https?:\/\/[^\/]+).*$/i, '$1') + link; if (/^https?:?\/\//i.test(link)) return link; base = base.replace(/\/[^\/]*(\?.*)?$/, '') + '/'; return base + link; } function testUrl(url) { if (!/\.m3u8?(\?.*)?$/i.test(url)) return testSpeed(url); var errorFn = function errorFn(e) { html$3.find('#speedtest_status').html(Lang.translate('network_error')); }; xmlHTTP = new XMLHttpRequest(); $.ajax({ url: url, cache: false, dataType: 'text', xhr: function xhr() { return xmlHTTP; }, success: function success(data) { if (data.substr(0, 7) !== '#EXTM3U') return errorFn(); var i = 0, links = [], bandwidth = 0, setLink = false, m, l = data.split(/\r?\n/); data = null; for (; links.length < 100 && i < l.length; i++) { if (!!(m = l[i].match(/^#EXTINF:\s*(-?\d+(\.\d*)?)\s*,.*$/))) { setLink = true; } else if (!!(m = l[i].match(/^#EXT-X-STREAM-INF:(.+,)?\s*BANDWIDTH=(\d+)\s*(,.+)?$/))) { if (bandwidth < parseInt(m[2])) { bandwidth = parseInt(m[2]); setLink = true; } else setLink = false; } else if (setLink && !!(m = l[i].match(/^[^#].+$/i))) { links.push(normalizeUrl(xmlHTTP.responseURL, m[0].trim())); setLink = false; } } if (links.length === 0) return errorFn(); if (bandwidth > 0) return testUrl(links.pop()); testM3u8Stream(links); }, error: errorFn }); } function testM3u8Stream(links) { if (links.length === 0) { return html$3.find('#speedtest_status').html(Lang.translate('network_error')); } var status = html$3.find('#speedtest_status'); var time; var speed = 0, speedMbps = 0; var currentSegment = 0; var maxSegments = links.length; // Берем все сегменты var totalBytes = 0; // Общий счетчик байт var startTime; // Время начала всего теста status.innerHTML = Lang.translate('speedtest_connect'); graph = [[-250, -250]]; setSpeed(0); function loadNextSegment() { if (currentSegment >= maxSegments) { // Завершаем тест setSpeed(speedMbps); status.innerHTML = Lang.translate('speedtest_ready'); if (typeof active$1.onEnd === 'function') { active$1.onEnd.apply(self, [speedMbps, xmlHTTP]); } return; } var segmentUrl = links[currentSegment]; console.log('Speed', 'testM3u8', 'loading segment', currentSegment + 1, '/', maxSegments, segmentUrl); xmlHTTP = new XMLHttpRequest(); xmlHTTP.open('GET', Utils$1.addUrlComponent(segmentUrl, 'vr=' + Date.now()), true); xmlHTTP.responseType = 'arraybuffer'; xmlHTTP.onprogress = function (e) { if (!time || time === true) return; var totalTime = e.timeStamp - startTime; // Время от начала всего теста var currentBytes = totalBytes + e.loaded; // Общие загруженные байты // Вычисляем скорость от начала теста speed = Math.ceil(currentBytes * 8000 / totalTime); speedMbps = speed / 1000 / 1000; // График строим по общему времени и данным var x = Math.max(Math.min(totalTime, 1e4) * 500 / 1e4, Math.min(currentBytes, 3e8) * 500 / 3e8) - 250; var y = -(speed2deg(speedMbps) / 4 + 250); graph.push([x.toFixed(1), y.toFixed(1)]); setSpeed(speedMbps); if (totalTime >= 1e4 || currentBytes > 3e8) xmlHTTP.abort(); }; xmlHTTP.onreadystatechange = function (e) { if (xmlHTTP.readyState === 2) { if (currentSegment === 0) { // Устанавливаем время начала только для первого сегмента time = e.timeStamp; startTime = e.timeStamp; status.innerHTML = Lang.translate('speedtest_test'); // Общий таймаут на весь тест tout = setTimeout(function () { xmlHTTP.abort(); }, 15e3); } } }; var endSegment = function endSegment(e) { if (xmlHTTP.response) { totalBytes += xmlHTTP.response.byteLength; } currentSegment++; loadNextSegment(); }; xmlHTTP.onload = endSegment; xmlHTTP.onabort = function (e) { clearTimeout(tout); setSpeed(speedMbps); status.innerHTML = Lang.translate('speedtest_ready'); if (typeof active$1.onEnd === 'function') { active$1.onEnd.apply(self, [speedMbps, xmlHTTP]); } }; xmlHTTP.onerror = endSegment; xmlHTTP.send(); } loadNextSegment(); } function testSpeed(url) { var context = this; var status = html$3.find('#speedtest_status'); var time; status.innerHTML = Lang.translate('speedtest_connect'); graph = [[-250, -250]]; var speed = 0, speedMbps = 0; setSpeed(0); xmlHTTP = new XMLHttpRequest(); xmlHTTP.open('GET', Utils$1.addUrlComponent(url, 'vr=' + new Date() * 1), true); if (active$1.login && active$1.password) xmlHTTP.setRequestHeader("Authorization", "Basic " + Base64.encode(active$1.login + ":" + active$1.password)); xmlHTTP.responseType = 'arraybuffer'; xmlHTTP.onprogress = function (e) { if (!time || time === true) return; var load = e.timeStamp - time; speed = Math.ceil(e.loaded * 8000 / load); // Бит в секунду speedMbps = speed / 1000 / 1000; var x = Math.max(Math.min(load, 1e4) * 500 / 1e4, Math.min(e.loaded, 3e8) * 500 / 3e8) - 250, y = -(speed2deg(speedMbps) / 4 + 250); graph.push([x.toFixed(1), y.toFixed(1)]); setSpeed(speedMbps); if (load >= 1e4 || e.loaded > 3e8) xmlHTTP.abort(); }; xmlHTTP.onreadystatechange = function (e) { if (xmlHTTP.readyState === 2) { time = e.timeStamp; status.innerHTML = Lang.translate('speedtest_test'); tout = setTimeout(function () { xmlHTTP.abort(); }, 15e3); } }; var endTest = function endTest(e) { clearTimeout(tout); setSpeed(speedMbps); status.innerHTML = Lang.translate('speedtest_ready'); time = false; if (typeof active$1.onEnd === 'function') active$1.onEnd.apply(context, [speedMbps, xmlHTTP]); }; xmlHTTP.onload = endTest; xmlHTTP.onabort = endTest; xmlHTTP.onerror = endTest; xmlHTTP.send(); } function toggle() { Controller.add('speedtest', { toggle: function toggle() { Controller.clear(); }, back: close$1 }); Controller.toggle('speedtest'); } /** * Закрыть тест скорости * @returns {void} */ function close$1() { if (xmlHTTP) xmlHTTP.abort(); clearTimeout(tout); html$3.remove(); html$3 = false; xmlHTTP = false; if (active$1.onBack) active$1.onBack();else Controller.toggle(controll$1); active$1 = {}; } var Speedtest = { init: init$h, close: close$1, start: start$2 }; var html$2, text; var processing = []; /** * Инициализирует слой прогресса обработки * @returns {void} */ function init$g() { html$2 = $('
    '); text = html$2.find('.processing__text'); update(); } /** * Обновляет слой прогресса обработки * @returns {void} */ function update() { if (processing.length) { text.text(processing.length > 1 ? processing.length : '1 / ' + Math.round(processing[0].percent) + '%'); var complite = processing.find(function (a) { return a.percent >= 100; }); if (complite) Lampa.Arrays.remove(processing, complite); requestAnimationFrame(update); } else html$2.addClass('hide'); } /** * Добавляет процесс обработки * @param {string} id - уникальный идентификатор процесса * @param {number} percent - процент выполнения процесса (0-100) * @returns {void} */ function push$1(id, percent) { var find = processing.find(function (a) { return a.id == id; }); if (!find) { processing.push({ id: id, percent: percent, start: Date.now() }); html$2.removeClass('hide'); update(); } else find.percent = percent; } /** * Возвращает HTML слой прогресса обработки * @param {boolean} [js=false] - возвращать как JS объект или jQuery * @returns {jQuery|HTMLElement} */ function render(js) { return js ? html$2[0] : html$2; } var Processing = { init: init$g, push: push$1, render: render }; function init$f() { if (!window.lampa_settings.iptv) return; var components = ['tmdb', 'plugins', 'content_rows']; var titles = ['card_interfice_type']; var params = ['light_version', 'card_interfice_type', 'card_interfice_reactions', 'player_timecode', 'pages_save_total', 'device_name', 'playlist_next', 'background_type', 'card_views_type', 'hide_outside_the_screen', 'card_interfice_cover', 'card_interfice_poster', 'start_page', 'source', 'card_quality', 'card_episodes', 'player']; Lang.add({ about_text: { ru: 'Наслаждайтесь любимыми телепередачами, фильмами и спортивными событиями на вашем устройстве с нашим приложением для просмотра IPTV каналов. Наше приложение просто в использовании и позволяет настроить список избранных каналов для быстрого доступа к любимым программам. Просматривайте телевизор в любое время и в любом месте, не пропуская ни одного важного момента благодаря нашему надежному и удобному IPTV приложению.', uk: 'Насолоджуйтесь улюбленими телепередачами, фільмами та спортивними подіями на вашому пристрої з нашим програмою для перегляду IPTV каналів. Наша програма просто у використанні та дозволяє налаштувати список вибраних каналів для швидкого доступу до улюблених програм. Переглядайте телевізор у будь-який час та в будь-якому місці, не пропускаючи жодного важливого моменту завдяки нашому надійному та зручному IPTV додатку.', be: 'Атрымлівайце асалоду ад любімымі тэлеперадачамі, фільмамі і спартыўнымі падзеямі на вашым прыладзе з нашым дадаткам для прагляду IPTV каналаў. Наша дадатак проста ў выкарыстанні і дазваляе наладзіць спіс абраных каналаў для хуткага доступу да любімых праграм. Праглядайце тэлевізар у любы час і ў любым месцы, не прапускаючы ніводнага важнага моманту дзякуючы нашаму надзейнаму і зручнаму IPTV з дадаткам.', en: 'Enjoy your favorite TV shows, movies and sports on your device with our IPTV channel viewer app. Our application is easy to use and allows you to set up your favorite channel list for quick access to your favorite programs. Watch TV anytime, anywhere without missing a single important moment thanks to our reliable and convenient IPTV application.', pt: 'Aproveite seus programas de TV, filmes e esportes favoritos em seu dispositivo com nosso aplicativo visualizador de canais IPTV. Nosso aplicativo é fácil de usar e permite que você configure sua lista de canais favoritos para acesso rápido aos seus programas favoritos. Assista TV a qualquer hora, em qualquer lugar, sem perder um único momento importante, graças ao nosso aplicativo IPTV confiável e conveniente.', zh: '使用我们的 IPTV 频道查看器应用程序在您的设备上欣赏您最喜爱的电视节目、电影和体育节目。 我们的应用程序易于使用,并允许您设置您最喜爱的频道列表,以便快速访问您最喜爱的节目。 借助我们可靠且方便的 IPTV 应用程序,随时随地观看电视,不会错过任何一个重要时刻。' }, empty_title_two: { ru: 'Произошла ошибка', uk: 'Виникла помилка', be: 'Адбылася памылка', en: 'An error has occurred', pt: 'Оcorreu um erro', zh: '发生了错误' }, empty_text_two: { ru: 'К сожалению, приложение не загружается. Рекомендуем попробовать перезагрузить его.', uk: 'На жаль, програма не завантажується. Рекомендуємо спробувати перезавантажити його.', be: 'Нажаль, прыкладанне не загружаецца. Рэкамендуем паспрабаваць перазагрузіць яго.', en: 'Unfortunately, the application does not load. We recommend that you try rebooting it.', pt: 'Infelizmente, o aplicativo não carrega. Recomendamos que você tente reiniciá-lo.', zh: '不幸的是,该应用程序未加载。 我们建议您尝试重新启动它。' } }); window.iptvClearSettingsFunction = function (e) { titles.forEach(function (t) { var param = $('[data-name="' + t + '"]', e.body).prev(); if (param.length && param.hasClass('settings-param-title')) param.remove(); }); $(components.map(function (c) { return '[data-component="' + c + '"]'; }).join(','), e.body).remove(); $(params.map(function (c) { return '[data-name="' + c + '"]'; }).join(','), e.body).remove(); }; window.iptvClearFunction = function () { $('.menu .menu__list:eq(0)').empty(); $('.head .open--notice, .head .open--search, .head .open--feed, .head .open--premium').addClass('hide'); window.iptvClearFunction = function () {}; }; setTimeout(function () { window.iptvClearFunction(); }, 10); Lampa.Settings.listener.follow('open', window.iptvClearSettingsFunction); Utils$1.putScriptOfMirrors([Utils$1.protocol() + object$2.cub_domain + '/plugin/iptv'], function () { console.log('IPTV', 'load from lampa', 'complite'); }, function () { console.log('IPTV', 'load from lampa', 'error'); }, function () { console.log('IPTV', 'load from lampa', 'success'); }, false); } var Iptv = { init: init$f }; function init$e() { var keyboard_mode = false; // Style to disable hover effects var style = document.createElement("style"); style.innerHTML = "* { pointer-events: none !important; }"; // Disable hover on any keyboard event document.addEventListener("keydown", function (event) { var tagName = ''; try { tagName = event.target.tagName.toLowerCase(); } catch (e) {} if (!keyboard_mode && !(tagName == "input" || tagName == "textarea")) { console.log('HoverSwitcher', 'Keyboard moved, disabling pointer events'); keyboard_mode = true; document.head.appendChild(style); } }); function mouse() { if (keyboard_mode) { console.log('HoverSwitcher', 'Mouse moved, enabling pointer events'); keyboard_mode = false; document.head.removeChild(style); } } // Re-enable hover on mouse movement or touch document.addEventListener("mousemove", mouse); document.addEventListener("touchmove", mouse); } var HoverSwitcher = { init: init$e }; /** * Последовательное выполнение задач * @param {Array} data - массив данных для обработки * @property {function(object, function)} onProgress - функция, которая будет вызвана для каждого элемента массива, принимает элемент и функцию next для продолжения * @property {function()} onComplite - функция, которая будет вызвана по завершении всех задач * @example * let task = new Task([1,2,3,4,5]) * task.onProgress = (item, next)=>{ * console.log('Process item:', item) * next() //обязательно вызвать для продолжения * } * task.onComplite = ()=>{ * console.log('All items processed') * } * task.start() //начать выполнение */ function Task$1(data) { this.data = data; this.work = 0; this.need = data.length; this.check = function () { if (this.work >= this.need) this.onComplite(); }; this.next = function () { var _this = this; this.onProgress(this.data[this.work], function () { _this.work++; if (_this.work < _this.need) _this.next();else _this.check(); }); }; this.start = function () { if (this.need) this.next();else this.check(); }; } var queue_calls = []; var secondary_calls = []; function queue(call) { queue_calls.push(call); } function secondary(call) { secondary_calls.push(call); } /** * Запуск очереди загрузки * 1. Выполняет задачи из очереди queue_calls последовательно * 2. После завершения всех задач из очереди queue_calls выполняет задачи из очереди secondary_calls параллельно */ function start$1() { var task = new Task$1(queue_calls); task.onProgress = function (call, next) { var called = false; var launch = function launch() { if (!called) next(); called = true; }; var timer = setTimeout(launch, 16000); try { call(function () { clearTimeout(timer); launch(); }); } catch (e) { console.warn('Loader', 'queue task error:', e.message, e.stack); clearTimeout(timer); launch(); } }; task.onComplite = function () { var progress = new Progress(); progress.append(secondary_calls); progress.start(function () {}); }; task.start(); } var Task = { queue: queue, secondary: secondary, start: start$1 }; function init$d() { var loader; var waite; SettingsApi.addParam({ component: 'more', param: { type: 'title' }, field: { name: Lampa.Lang.translate('menu_console') } }); SettingsApi.addParam({ component: 'more', param: { name: 'export', type: 'button' }, field: { name: Lang.translate('settings_cub_backup_export') }, onChange: function onChange() { if (waite) return; waite = true; loader.removeClass('hide'); push().then(function (code) { Modal.open({ title: '', html: $('
    ' + Lang.translate('account_export_secuses') + ': ' + code + '
    '), size: 'small', onBack: closeModal }); })["catch"](function (code) { Noty.show(Lang.translate('account_export_fail' + (code && typeof code == 'number' ? '_' + code : ''))); })["finally"](function () { waite = false; loader.addClass('hide'); }); }, onRender: function onRender(item) { loader = $('
    '); item.append(loader); } }); var terminal_elem; var terminal_code = Storage.get('terminal_access', ''); function terminalDrawCode() { if (!terminal_elem) return; terminal_code = Storage.get('terminal_access', ''); terminal_elem.find('.settings-param__descr').text(terminal_code ? Lang.translate('terminal_code') + ': ' + terminal_code : Lang.translate('terminal_no_access')); } function terminalWriteCode(code) { Storage.set('terminal_access', code); terminalDrawCode(); Socket.send('terminal', {}); } SettingsApi.addParam({ component: 'more', param: { name: 'terminal', type: 'button' }, field: { name: Lang.translate('terminal_title'), description: Lang.translate('terminal_no_access') }, onChange: function onChange(a, b, c) { Modal.open({ title: '', html: $('
    ' + Lang.translate('terminal_text') + '
    '), size: 'medium', buttons: [{ name: Lang.translate(terminal_code ? 'terminal_update' : 'terminal_confirm'), onSelect: function onSelect() { closeModal(); terminalWriteCode(Math.floor(100000 + Math.random() * 900000).toString()); } }, { name: Lang.translate('terminal_deny'), onSelect: function onSelect() { closeModal(); terminalWriteCode(''); } }], onBack: closeModal }); }, onRender: function onRender(item) { terminal_elem = item; terminalDrawCode(); } }); } function closeModal() { Modal.close(); Controller.toggle('settings_component'); } function push() { return new Promise(function (resolve, reject) { var file; var data = JSON.stringify(Console["export"]()); try { file = new File([data], "lampa_logs.json", { type: "text/plain" }); } catch (e) {} if (!file) { try { file = new Blob([data], { type: 'text/plain' }); file.lastModifiedDate = new Date(); } catch (e) { reject(); } } if (file) { var formData = new FormData($('
    ')[0]); formData.append("file", file, "lampa_logs.json"); $.ajax({ url: Utils$1.protocol() + object$2.cub_domain + '/api/lampa/logs/write', type: 'POST', data: formData, async: true, cache: false, timeout: 10000, contentType: false, enctype: 'multipart/form-data', processData: false, success: function success(j) { resolve(j.code); }, error: function error(e) { reject(e.rresponseJSON ? e.responseJSON.code : 0); } }); } }); } var Logs = { init: init$d }; var html$1; var controll; var active = {}; /** * Открыть менеджер хранилища * @param {object} [params] - параметры * @param {function} [params.onBack] - вызывается при выходе из менеджера * @returns {void} */ function open() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; active = params; controll = Controller.enabled().name; html$1 = $('
    '); var keys = Object.keys(localStorage); var hide = ['parental_', 'vast_', 'account_']; if (window.lampa_settings.hide_important_params) { keys = keys.filter(function (key) { return !hide.find(function (h) { return key.indexOf(h) == 0; }); }); } keys.sort(function (a, b) { return a.localeCompare(b); }); keys.forEach(function (key) { var value = Utils$1.shortText(localStorage.getItem(key), 50); // Не показывать метки времени if (parseInt(value) > 0 && (value + '').length == 13) return; var line = $('
    ' + key + ' ' + value + '
    '); line.on('hover:enter', function () { Select.show({ title: key, items: [{ title: Lang.translate('settings_remove'), action: 'delete' }], onSelect: function onSelect(selected) { Controller.toggle('modal'); if (selected.action == 'delete') { localStorage.removeItem(key); line.remove(); } }, onBack: function onBack() { Controller.toggle('modal'); } }); }); html$1.append(line); }); Modal.open({ title: 'StorageManager', size: 'large', html: html$1, onBack: close }); } function close() { html$1.remove(); Modal.close(); if (active.onBack) active.onBack();else Controller.toggle(controll); active = {}; } var StorageMenager = { close: close, open: open }; var component$1 = 'data'; var icon$1 = "\n \n \n"; function init$c() { SettingsApi.addComponent({ component: component$1, icon: icon$1, name: Lang.translate('settings_rest_cache_all'), after: 'parental_control' }); var status; SettingsApi.addParam({ component: component$1, param: { type: 'button' }, field: { name: Lampa.Lang.translate('settings_rest_cache_calculate') }, onRender: function onRender(item) { status = $("
    "); item.append(status); }, onChange: function onChange(a, b) { status.removeClass('hide'); Storage.getsize(function (size) { status.text(Lang.translate('title_left') + ' - ' + Utils$1.bytesToSize(size)); }); } }); SettingsApi.addParam({ component: component$1, param: { type: 'title' }, field: { name: Lampa.Lang.translate('more') } }); SettingsApi.addParam({ component: component$1, param: { type: 'button' }, field: { name: Lampa.Lang.translate('extensions_edit') }, onChange: function onChange() { StorageMenager.open({ onBack: function onBack() { Controller.toggle('settings_component'); } }); } }); SettingsApi.addParam({ component: component$1, param: { type: 'button' }, field: { name: Lampa.Lang.translate('fav_clear_title') }, onChange: function onChange() { Favorite.clear('history'); Noty.show(Lang.translate('torrent_error_made')); } }); SettingsApi.addParam({ component: component$1, param: { type: 'button' }, field: { name: Lampa.Lang.translate('settings_rest_cache') }, onChange: function onChange() { Select.show({ title: Lang.translate('settings_rest_cache'), items: [{ title: Lang.translate('settings_rest_cache_only'), subtitle: Lang.translate('settings_rest_cache_only_descr') }, { title: Lang.translate('settings_rest_cache_all'), subtitle: Lang.translate('settings_rest_cache_all_descr'), full: true }], onSelect: function onSelect(a) { Controller.toggle('settings_component'); Storage.clear(a.full); Cache.clearAll(); }, onBack: function onBack() { Controller.toggle('settings_component'); } }); } }); } var DataBase = { init: init$c }; var EmptyMap = { Ai: Ai, Simple: Simple, Router: Router }; var Helper = new Mask(Arrays.getKeys(EmptyMap)); Helper.MASK.base = Helper.only('Simple'); var classes = { Card: Card$3, Main: Full$1, Category: Full, Line: Line$4, Company: Company, Discuss: Discuss, Episode: Episode, Person: Person, Register: Register, Season: Season, CardParser: CardParser, Empty: Empty }; var modules = { Card: Helper$9, Main: Helper$7, Category: Helper$4, Line: Helper$b, Company: Helper$3, Discuss: Helper$5, Episode: Helper$a, Person: Helper$6, Register: Helper$8, Season: Helper$2, Empty: Helper }; var maps = { Card: CardMap, Episode: EpisodeMap, Main: MainMap, Category: CategoryMap, Line: LineMap, Company: CompanyMap, Discuss: DiscussMap, Person: PersonMap, Register: RegisterMap, Season: SeasonMap, Empty: EmptyMap }; var None = /*#__PURE__*/function (_Constructor) { _inherits(None, _Constructor); var _super = _createSuper(None); function None() { _classCallCheck(this, None); return _super.apply(this, arguments); } return None; }(Constructor({})); function make(class_name) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var createModule = arguments.length > 2 ? arguments[2] : undefined; var createName = get(class_name, data, createModule); var createClass = new createName(data); if (data.params.emit && _typeof(data.params.emit) == 'object' && typeof createClass.use == 'function') { createClass.use(data.params.emit); if (typeof data.params.emit.onInit == 'function') { data.params.emit.onInit(createClass); } } return createClass; } function get(class_name) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var createModule = arguments.length > 2 ? arguments[2] : undefined; if (typeof classes[class_name] == 'undefined') { console.log('Maker', 'error', 'no class', class_name); return None; } else { Arrays.extend(data, { params: {} }); if (typeof createModule !== 'undefined') { if (typeof modules[class_name] !== 'undefined') { data.params.module = createModule(modules[class_name]); } } return classes[class_name]; } } function module$1(class_name) { if (typeof modules[class_name] !== 'undefined') return modules[class_name]; console.log('Maker', 'error', 'no module', class_name); return new Mask([]); } function map(class_name) { if (typeof maps[class_name] == 'undefined') return {}; return maps[class_name]; } function list() { return Object.keys(classes); } var Maker = { get: get, make: make, module: module$1, list: list, map: map }; var torrent_net = new Request(); /** * Инициализация работы с локальным торрент сервером, проверка доступности и настройка * @returns {void} */ function init$b() { Storage.listener.follow('change', function (e) { if (e.name == 'torrserver_url') check(e.name); if (e.name == 'torrserver_url_two') check(e.name); if (e.name == 'torrserver_use_link') check(e.value == 'one' ? 'torrserver_url' : 'torrserver_url_two'); }); Settings.listener.follow('open', function (e) { if (e.name == 'server') { check(Storage.field('torrserver_use_link') == 'one' ? 'torrserver_url' : 'torrserver_url_two'); } else torrent_net.clear(); }); } function check(name) { if (Platform.is('android') && !Storage.field('internal_torrclient')) return; var item = $('[data-name="' + name + '"]').find('.settings-param__status').removeClass('active error wait').addClass('wait'); var url = Storage.get(name); if (url) { torrent_net.timeout(10000); var head = { dataType: 'text' }; var auth = Storage.field('torrserver_auth'); if (auth) { head.headers = { Authorization: "Basic " + Base64.encode(Storage.get('torrserver_login') + ':' + Storage.value('torrserver_password')) }; } torrent_net["native"](Utils$1.checkEmptyUrl(Storage.get(name)), function () { item.removeClass('wait').addClass('active'); }, function (a, c) { if (a.status == 401) { item.removeClass('wait').addClass('active'); Noty.show(Lang.translate('torrent_error_check_no_auth') + ' - ' + url, { time: 5000 }); } else { item.removeClass('wait').addClass('error'); Noty.show(torrent_net.errorDecode(a, c) + ' - ' + url, { time: 5000 }); } }, false, head); } } var ServiceTorserver = { init: init$b }; /** * Инициализация отправки информации о просмотренных фильмах/сериалах на сервер, для обновления популярности * @returns {void} */ function init$a() { Favorite.listener.follow('add,added', function (e) { if (e.where == 'history' && e.card.id && (e.card.source == 'tmdb' || e.card.source == 'cub')) { $.get(Utils$1.protocol() + 'tmdb.' + object$2.cub_domain + '/watch?id=' + e.card.id + '&cat=' + (e.card.original_name ? 'tv' : 'movie')); } }); } var ServiceWatched = { init: init$a }; /** * Инициализация дополнительных настроек * @returns {void} */ function init$9() { Settings.listener.follow('open', function (e) { if (e.name == 'more' && window.location.protocol == 'https:') { $('[data-name="protocol"]', e.body).remove(); } if (e.name == 'interface' && window.lampa_settings.lang_use) { $('.settings-param:eq(0)', e.body).on('hover:enter', function () { LangChoice.open(function (code) { Storage.set('language', code, true); Storage.set('tmdb_lang', code, true); window.location.reload(); }, function () { Controller.toggle('settings_component'); }); }).find('.settings-param__value').text(Lang.translate(Lang.codes()[Storage.get('language', 'ru')])); } if (e.name == 'main' && Platform.is('apple_tv')) { var append = e.body.find('.appletv-setting'); if (!append.length) { append = $("
    \n
    \n \n
    \n
    ".concat(Lang.translate('menu_settings'), "
    \n
    ")); e.body.find('.scroll__body > div').append(append); } append.unbind('hover:enter').on('hover:enter', function () { window.open('lampa://showadvancedmenu'); }); } }); } var ServiceSettings = { init: init$9 }; /** * Инициализация дополнительных библиотек * @returns {void} */ function init$8() { var include = []; // Видео библиотеки include = include.concat(['hls/hls.js', 'dash/dash.js', 'qrcode/qrcode.js'].map(function (lib) { return window.location.protocol == 'file:' || window.location.href.indexOf('chrome-extension') > -1 ? object$2.github_lampa + 'vender/' + lib : './vender/' + lib; })); // YouTube IFrame API // больше не актуально, так как youtube плеер теперь использует собственный мост для взаимодействия с iframe, и не зависит от глобального объекта YT // if(window.youtube_lazy_load && window.lampa_settings.youtube){ // include.push(Utils.protocol() + 'youtube.com/iframe_api') // } // Плагины различные if (!window.lampa_settings.iptv && window.lampa_settings.services) { include.push(Utils$1.protocol() + object$2.cub_domain + '/plugin/sport'); include.push(Utils$1.protocol() + object$2.cub_domain + '/plugin/tsarea'); } // Плагин Shots if (window.location.hostname !== 'localhost' && !window.lampa_settings.iptv) include.push(Utils$1.protocol() + object$2.cub_domain + '/plugin/shots'); Utils$1.putScriptOfMirrors(include, function () {}); } var ServiceLibs = { init: init$8 }; var open_about = false; var press_button = 0; var buttons = ['enabled', 'nopremium', 'nodemo', 'ads', 'fps']; var icon = "\n\n\n\n\n\n\n"; function init$7() { Modal.listener.follow('toggle', function (e) { open_about = e.active.about; if (e.active.about) { e.html.find('.modal__body').on('click', function (e) { if (DeviceInput.canClick(e.originalEvent)) press(); }); } }); Modal.listener.follow('preshow,close', function (e) { open_about = false; press_button = 0; }); Keypad.listener.follow('enter', press); Storage.listener.follow('change', function (e) { if (e.name == 'developer_nodemo') Storage.set('remove_white_and_demo', e.value, true); }); if (Storage.get('developer_enabled', 'false')) activate(); } function press() { if (!open_about || window.lampa_settings.developer.enabled) return; press_button++; if (press_button == 10) { Bell.push({ text: Lang.translate('developer_trigger_help_1'), icon: icon }); } else if (press_button == 20) { Bell.push({ text: Lang.translate('developer_trigger_help_2'), icon: icon }); } else if (press_button == 30) { Bell.push({ text: Lang.translate('developer_trigger_help_3'), icon: icon }); } else if (press_button == 40) { Storage.set('developer_enabled', 'true'); activate(); Bell.push({ text: Lang.translate('developer_trigger_help_4'), icon: icon }); } } function params() { var component = 'developer'; SettingsApi.addComponent({ component: component, icon: icon, name: Lang.translate('title_developer') }); var display = Arrays.clone(buttons); if (!Account$1.hasPremium()) { Arrays.remove(display, 'ads'); } display.forEach(function (name) { Lampa.SettingsApi.addParam({ component: component, param: { name: 'developer_' + name, type: 'trigger', "default": false }, field: { name: Lampa.Lang.translate('developer_param_' + name) }, onChange: function onChange(e) { window.lampa_settings.developer[name] = Storage.get('developer_' + name, 'false'); } }); }); var values = {}; object$2.cub_mirrors.forEach(function (domain) { values[domain] = domain; }); Lampa.SettingsApi.addParam({ component: component, param: { name: 'cub_domain', type: 'select', values: values, "default": object$2.cub_domain }, field: { name: Lampa.Lang.translate('settings_cub_domain') }, onChange: function onChange(e) { Storage.set('cub_alive', Utils$1.protocol() + object$2.cub_domain, true); } }); } function activate() { buttons.forEach(function (name) { window.lampa_settings.developer[name] = Storage.get('developer_' + name, 'false'); }); SettingsApi.removeComponent('developer'); if (window.lampa_settings.developer.enabled) params(); } var ServiceDeveloper = { init: init$7 }; /** * Инициализация быстрого доступа к закладкам через цветные кнопки пульта * @returns {void} */ function init$6() { var color_keys = { '406': 'history', '405': 'wath', '404': 'like', '403': 'book' }; Keypad.listener.follow('keydown', function (e) { if (!Player.opened()) { if (color_keys[e.code]) { var type = color_keys[e.code]; Activity.push({ url: '', title: type == 'book' ? Lang.translate('title_book') : type == 'like' ? Lang.translate('title_like') : type == 'history' ? Lang.translate('title_history') : Lang.translate('title_wath'), component: 'favorite', type: type, page: 1 }); } } }); } var ServiceRemoteFavorites = { init: init$6 }; /** * Инициализация DMCA, блокировка карточек к показу по требованию правообладателей * @returns {void} */ function init$5() { if (!window.lampa_settings.disable_features.dmca) { // Cache.getData('other', 'dcma', 60 * 24 * 10).then((result)=>{ // if(result && Arrays.isArray(result)) window.lampa_settings.dcma = result // else{ Lampa.Network.silent(Utils$1.protocol() + 'tmdb.' + object$2.cub_domain + '/blocked', function (dcma) { window.lampa_settings.dcma = dcma; //Cache.rewriteData('other', 'dcma', dcma).catch((e)=>{}) }); // } // }).catch(e=>{}) } } var ServiceDMCA = { init: init$5 }; /** * Инициализация DMCA, блокировка карточек к показу по требованию правообладателей * @returns {void} */ function init$4() { if (!window.lampa_settings.disable_features.lgbt) { Cache.getData('other', 'lgbt', 60 * 24 * 10).then(function (result) { if (result && Arrays.isObject(result)) window.lampa_settings.lgbt = result;else { Lampa.Network.silent(Utils$1.protocol() + 'tmdb.' + object$2.cub_domain + '/lgbt.json', function (lgbt) { var map = {}; lgbt.forEach(function (item) { map[item.id + '_' + item.type] = true; }); window.lampa_settings.lgbt = map; Cache.rewriteData('other', 'lgbt', map)["catch"](function (e) {}); }); } })["catch"](function (e) {}); if (!vpn.is(['ru', 'by'])) { Lampa.SettingsApi.addParam({ component: 'more', param: { name: 'lgbt_content_block', type: 'trigger', "default": false }, field: { name: Lampa.Lang.translate('settings_lgbt_content_block') } }); } } } var ServiceLGBT = { init: init$4 }; function init$3() { var counter = Template.elem('div', { "class": 'head__fps-counter' }); var canvas = Template.elem('canvas', { "class": 'head__fps-graph' }); var ctx = canvas.getContext("2d"); var html = Template.elem('div', { "class": 'head__fps', children: [canvas, counter] }); var last_time = typeof performance !== 'undefined' ? performance.now() : Date.now(); var fps = 0; var smoothing = 0.8; var history = []; var max_history = 0; function updateFPS(now) { var delta = now - last_time; last_time = now; var current_fps = 1000 / delta; fps = fps * smoothing + current_fps * (1 - smoothing); counter.textContent = fps.toFixed(1); // Добавляем FPS в историю history.push(fps); if (history.length > max_history) history.shift(); drawGraph(); if (window.lampa_settings.developer.fps) requestAnimationFrame(updateFPS); } function drawGraph() { var w = canvas.width; var h = canvas.height; var m = 60; // Ищем максимальный FPS в истории для масштабирования history.forEach(function (v) { m = Math.max(m, v); }); // небольшой запас сверху m += m * 0.2; // Масштаб по вертикали var scale = h / m; ctx.clearRect(0, 0, w, h); var gradient = ctx.createLinearGradient(0, 0, 0, h); gradient.addColorStop(0, "rgba(0,255,0,0.4)"); gradient.addColorStop(1, "rgba(0,255,0,0)"); ctx.beginPath(); for (var i = 0; i < history.length; i++) { var y = h - history[i] * scale; if (i === 0) ctx.moveTo(i, y);else ctx.lineTo(i, y); } // Замыкаем форму до низа графика ctx.lineTo(history.length, h); ctx.lineTo(0, h); ctx.closePath(); // Заливка ctx.fillStyle = gradient; ctx.fill(); // Контур линии FPS ctx.beginPath(); for (var _i = 0; _i < history.length; _i++) { var _y = h - history[_i] * scale; if (_i === 0) ctx.moveTo(_i, _y);else ctx.lineTo(_i, _y); } ctx.strokeStyle = "#0f0"; ctx.lineWidth = 1.5; ctx.stroke(); } function start() { Head.render(true).find('.head__title').after(html); canvas.width = $(canvas).width(); canvas.height = $(canvas).height(); history = []; max_history = canvas.width; requestAnimationFrame(updateFPS); } Storage.listener.follow('change', function (e) { if (e.name === 'developer_fps') { if (e.value == 'true') start();else { html.remove(); } } }); if (window.lampa_settings.developer.fps) start(); } var ServiceFPS = { init: init$3 }; var events = [{ start: '10-25T00:00:00', end: '10-31T23:59:00', name: 'halloween' }, { start: '11-24T00:00:00', end: '11-30T23:59:00', name: 'black-friday' }, // Новогодние { start: '12-20T00:00:00', end: '01-04T23:59:00', name: 'snow' }, { start: '01-01T00:00:00', end: '01-03T23:59:00', name: 'new-year' }, { start: '03-06T00:00:00', end: '03-10T23:59:00', name: 'womens-day' }]; function init$2() { if (!window.lampa_settings.services) return; events.forEach(function (event) { var nowDate = new Date(); var now = nowDate.getTime(); var year = nowDate.getFullYear(); var start_time = new Date(year + '-' + event.start).getTime(); var end_time = new Date(year + '-' + event.end).getTime(); // событие пересекает границу года (например 12-20 -> 01-10) var crossYear = end_time < start_time; if (crossYear) { // конец в следующем году end_time = new Date(year + 1 + '-' + event.end).getTime(); // если сейчас "январская" часть окна, то старт должен быть в прошлом году if (now < start_time) { start_time = new Date(year - 1 + '-' + event.start).getTime(); end_time = new Date(year + '-' + event.end).getTime(); } } event.start_time = start_time; event.end_time = end_time; // обычно удобнее включать границы event.enabled = start_time <= now && now <= end_time; }); var enabled = events.filter(function (e) { return e.enabled; }); if (enabled.length) { Utils$1.putScriptOfMirrors(enabled.map(function (e) { return Utils$1.protocol() + object$2.cub_domain + '/plugin/' + e.name; }), function () {}); } } var ServiceEvents = { init: init$2 }; function init$1() { Storage.listener.follow('change', function (e) { if (e.name == 'account' || e.name == 'parental_control_personal' || e.name == 'source') setChildrenProfile(); }); Listener$1.follow('profile_check', setChildrenProfile); addStyle(); setChildrenProfile(); } function setChildrenProfile() { if (!permit.child) return; Storage.set('source', 'cub', true); Storage.add('parental_control_personal', 'account_profiles', true); Storage.add('parental_control_personal', 'settings', true); document.body.toggleClass('hide-menu', Boolean(permit.child)); } function addStyle() { var not = ['main', 'movie', 'tv', 'cartoon', 'catalog', 'filter', 'anime', 'favorite', 'history', 'timetable', 'subscribes', 'settings', 'about', 'console', 'edit']; var css = "\n body.hide-menu .menu__item:not([data-action=\"".concat(not.join('"]):not([data-action="'), "\"]){\n display: none;\n }\n "); var teg = document.createElement('style'); teg.type = 'text/css'; teg.appendChild(document.createTextNode(css)); document.body.appendChild(teg); } var ServiceChildren = { init: init$1 }; var max_wait_time = 300000; // 5 минут var component = 'remote_configuration'; var request_code = ''; var request_time = 0; var request_timer = false; var request_html = null; var request_instlall_log = []; /** * Инициализация компонента удалённой конфигурации */ function init() { if (window.lampa_settings.disable_features.remote_configuration) return; var icon = "\n \n \n \n \n \n \n \n \n "; SettingsApi.addComponent({ component: component, icon: icon, name: Lang.translate('remote_configuration_settings_title'), before: 'more' }); Settings.listener.follow('open', function (e) { if (e.name == component) { var waite = false; var body = e.body.find('.scroll__body > div'); var html = $("
    \n
    \n
    ".concat(Lang.translate('remote_configuration_registration_text'), "
    \n
    \n
    \n
    ").concat(Lang.translate('remote_configuration_open_button'), "
    \n\n
    \n
    \n
    ")); if (request_code) { html = $("
    \n
    \n
    ".concat(Lang.translate('remote_configuration_waite_text'), "
    \n
    \n
    \n
    \n
    \n
    \n ").concat(Lang.translate('remote_configuration_code_life'), " \n
    \n
    \n
    \n
    \n
    ")); var str = (request_code + '').split(''); str.forEach(function (num, i) { html.find('.account-modal-split__code').append("
    ".concat(num, "
    ")); }); request_html = html; } var button = $('.modal__button', html); var loading = $('.broadcast__scan', html); button.on('hover:enter', function () { if (waite) return; waite = true; loading.removeClass('hide'); Lampa.Network.silent(api() + 'registration', function (result) { request_code = result.code; request_time = Date.now(); Settings.update(); start(); waite = false; }, function () { Bell.push({ text: Lang.translate('remote_configuration_notice_nocode'), icon: icon }); loading.addClass('hide'); waite = false; }); }); body.append(html); } }); } function api() { return Utils$1.protocol() + object$2.cub_domain + '/api/remote-configuration/'; } /** * Запуск таймера для отслеживания времени действия кода */ function start() { if (!request_code) return; request_timer = setInterval(function () { var time = Date.now() - request_time; if (time > max_wait_time) { request_code = ''; request_time = 0; request_html = null; request_instlall_log = []; clearInterval(request_timer); if (Controller.enabled().name == 'settings_component') Settings.update(); } else if (request_html) { var sec_num = Math.round((max_wait_time - time) / 1000); var minutes = Math.trunc(sec_num % 3600 / 60); var seconds = Math.round(sec_num % 60); request_html.find('.timer').text((minutes ? minutes + ' ' + Lang.translate('time_m') + ' ' : '') + (seconds + ' ' + Lang.translate('time_s'))); // Показывать последние 4 действия request_html.find('.install-log').html(request_instlall_log.slice(-4).map(function (item) { return "
    ").concat(item.name, " ").concat(item.value, "
    "); }).join('')); } // Каждые 10 секунд делать запрос if (time % 10000 < 1000) accept(); }, 1000); } /** * Принимать запросы на установку расширений и изменение настроек */ function accept() { Lampa.Network.silent(api() + 'accept?code=' + request_code, function (result) { if (result.delivery.length) result.delivery.forEach(install); }); } /** * Устанавливать расширения и изменять настройки согласно запросу */ function install(row) { row.data.forEach(function (item) { console.log('Remote configuration', 'install', item); try { if (item.type == 'extension') { Arrays.extend(item, { author: '@lampa', status: 1 }); if (item.url && !Plugins.get().find(function (ext) { return ext.url == item.url; })) { Plugins.add(item); request_instlall_log.push({ name: Lang.translate('settings_main_plugins'), value: Utils$1.shortText(item.url, 30), icon: 'history' }); } } else if (item.type == 'storage') { Storage.set(item.name, item.value); request_instlall_log.push({ name: Lang.translate('title_settings'), value: Utils$1.shortText(item.name, 30), icon: 'settings' }); } else if (item.type == 'reload') { setTimeout(function () { window.location.reload(); }, 1000); } } catch (e) { console.error('Remote configuration', 'install error', e); } }); } var ServiceRemoteConfiguration = { init: init }; window.screen_width = window.innerWidth; window.screen_height = window.innerHeight; /** * Настройки приложения */ if (typeof window.lampa_settings == 'undefined') { window.lampa_settings = {}; } var torrents_use = true; var agent = navigator.userAgent.toLowerCase(); var conditions = [agent.indexOf("lampa_client_yasha") > -1, typeof AndroidJS !== 'undefined' && (AndroidJS.appVersion() + '').toLowerCase().indexOf('rustore') > -1 && !localStorage.getItem('parser_use')]; // Если есть условия из списка, то отключаем торренты, дабы пройти модерацию в сторе if (conditions.indexOf(true) >= 0) torrents_use = false; Arrays.extend(window.lampa_settings, { // Использовать сокеты для синхронизации данных socket_use: true, // Адрес сокета, по умолчанию лампа берет адреса из манифеста socket_url: undefined, // Обрабатывать сообщения сокетов socket_methods: true, // Использовать аккаунты CUB account_use: true, // Синхронизировать закладки, таймкоды и прочее account_sync: true, // Разрешить установку плагинов и расширений plugins_use: true, // Разрешить использование магазина расширений plugins_store: true, // Показывать кнопку торрентов torrents_use: torrents_use, // Отключить фитчи куба и лампы disable_features: { // Блокировку карточек dmca: false, // Блокировка ЛГБТ-контента lgbt: false, // Реакции reactions: false, // Обсуждения discuss: false, // ИИ ai: false, // Подписка на уведомления subscribe: false, // Черный список плагинов blacklist: false, // Подписка на актеров persons: false, // Вспомогатиленые сервисы на подписку према ads: false, // Трейлеры trailers: false, // Установка прокси для запросов install_proxy: false, // Удаленная конфигурация remote_configuration: false }, // Подключить другие языки интерфейса, по умолчанию только русский и английский lang_use: true, // Белая и пушистая лампа, для одобрения модерации white_use: false, // Режим только для чтения, без кнопок онлайн и расширений read_only: false, // Добавить список блокировки карточек, пример: [{"id":3566556,"cat":"movie"},...] dcma: false, // Добавить блокировку ЛГБТ контента, пример: [{"id":3566556,"type":"movie"},...] lgbt: false, // Добавлять в адресную строку название текущего экрана push_state: true, // Является ли приложение IPTV iptv: false, // Показать ленту feed: true, // Различные сервисы в приложении services: true, // Подключить YouTube API youtube: true, // Определять гео по IP, иначе будет RU geo: true, // Использовать поиск зеркал mirrors: true, // Режим разработчика developer: { enabled: false }, // Размывать постер для мобильных устройств, эффект стекла blur_poster: true, // Скрывать важные параметры в приложении hide_important_params: true, // Фикс для виджетов, чтобы не подгружались стили с github fix_widget: window.localStorage.getItem('fix_widget') ? true : false }); // Если отключили if (window.localStorage.getItem('remove_white_and_demo')) { window.lampa_settings.demo = false; window.lampa_settings.white_use = false; } // Если IPTV, то отключаем все лишнее if (window.lampa_settings.iptv) { window.lampa_settings.socket_use = false; window.lampa_settings.plugins_store = false; window.lampa_settings.plugins_use = false; window.lampa_settings.account_sync = false; window.lampa_settings.torrents_use = false; } /** * Делаем классы доступными в глобальной области видимости */ function initClass() { window.Lampa = { Listener: Subscribe$2(), Lang: Lang, Subscribe: Subscribe$2, Storage: Storage, Platform: Platform, Utils: Utils$1, Params: Params, Menu: Menu$1, Head: Head, Notice: Notice$1, NoticeClass: NoticeClass, NoticeClassLampa: NoticeLampa, Background: Background, Favorite: Favorite, Select: Select, Controller: Controller, Activity: Activity, Keypad: Keypad, Template: Template, Component: Component, Reguest: Request, Filter: Filter, Files: create$3, Explorer: Explorer$1, Scroll: Scroll, Empty: Empty, Arrays: Arrays, Noty: Noty, Player: Player, PlayerVideo: PlayerVideo, PlayerInfo: PlayerInfo, PlayerPanel: PlayerPanel, PlayerFooter: PlayerFooter, PlayerIPTV: PlayerIPTV, PlayerPlaylist: PlayerPlaylist, PlayerChat: PlayerChat, Timeline: Timeline$1, Modal: Modal, Api: Api, Settings: Settings, SettingsApi: SettingsApi, Android: Android, Card: Card, Info: create$2, Account: Account$1, Socket: Socket, Input: Input, Screensaver: Screensaver$2, Recomends: Recomends, TimeTable: TimeTable, Broadcast: Broadcast, Helper: Helper$1, InteractionMain: component$3, InteractionCategory: component$2, InteractionLine: create$1, Status: Status, Plugins: Plugins, Extensions: Extensions, Tizen: Tizen, Layer: Layer, Console: Console, Iframe: Iframe, Parser: Parser, Manifest: object$2, TMDB: TMDB$1, Base64: Base64, Loading: Loading$1, YouTube: YouTube, WebOSLauncher: WebOSLauncher, Event: Event, Search: Search, DeviceInput: DeviceInput, Worker: AppWorker, DB: IndexedDB, NavigationBar: NavigationBar, Endless: Endless, Color: Color, Cache: Cache, Torrent: Torrent, Torserver: Torserver, Speedtest: Speedtest, Processing: Processing, ParentalControl: ParentalControl, VPN: vpn, Bell: Bell, StorageMenager: StorageMenager, RemoteHelper: RemoteHelper, Network: new Request(), Maker: Maker, MaskHelper: Mask, ContentRows: ContentRows, Emit: Emit, Router: router, Timer: Timer }; } /** * Подготовка приложения к запуску */ function prepareApp() { if (window.prepared_app) return; LoadingProgress.init(); document.body.append(Noty.render()); Platform.init(); LoadingProgress.status('Platform init'); DeviceInput.init(); LoadingProgress.status('DeviceInput init'); Params.init(); LoadingProgress.status('Params init'); Controller.observe(); LoadingProgress.status('Controller observe init'); Console.init(); LoadingProgress.status('Console init'); Keypad.init(); LoadingProgress.status('Keypad init'); Layer.init(); LoadingProgress.status('Layer init'); HoverSwitcher.init(); // Передаем фокус в контроллер Navigator.follow('focus', function (event) { Controller.focus(event.elem); }); // Выход в начальном скрине Keypad.listener.follow('keydown', function (e) { if (window.appready || Controller.enabled().name == 'modal' || Platform.is('browser') || Platform.desktop()) return; if (e.code == 8 || e.code == 27 || e.code == 461 || e.code == 10009 || e.code == 88) App.modalClose(); }); LoadingProgress.status('Subscribe on keydown'); // Отключаем правый клик if (window.innerWidth > 1280) window.addEventListener("contextmenu", function (e) { return e.preventDefault(); }); App.loadStyle(); LoadingProgress.status('Loaded styles'); Layer.update(); LoadingProgress.status('Prepare ready'); window.prepared_app = true; } /** * Меню разработчика */ function developerApp(proceed) { var expect = true; var pressed = 0; var timer = setTimeout(function () { expect = false; proceed(); }, 1000); var check = function check() { pressed++; if (pressed === 3) { clearTimeout(timer); expect = false; Keypad.enable(); Developer.open(function () { Keypad.disable(); proceed(); }); console.log('Developer mode', 'on'); } }; var keydown = function keydown(event) { if (expect) { if (event.keyCode == 38 || event.keyCode == 29460 || event.keyCode == 50400012) check(); } else { document.removeEventListener('keydown', keydown); } }; $('.welcome').on('click', function (e) { if (expect && DeviceInput.canClick(e.originalEvent)) check(); }); window.addEventListener("keydown", keydown); } /** * Старт приложения */ function startApp() { if (window.appready || window.app_time_launch) return; window.app_time_launch = Date.now(); window.app_time_end = 0; // Стартуем LoadingProgress.status('Launching the application'); Lampa.Listener.send('app', { type: 'start' }); // Инициализируем классы Timer.init(); LoadingProgress.status('Timer init'); Storage.init(); LoadingProgress.status('Storage init'); Timeline$1.init(); LoadingProgress.status('Timeline init'); Mirrors.init(); LoadingProgress.status('Mirrors init'); Personal.init(); LoadingProgress.status('Personal init'); Head.init(); LoadingProgress.status('Head init'); Settings.init(); LoadingProgress.status('Settings init'); Select.init(); LoadingProgress.status('Select init'); Favorite.init(); LoadingProgress.status('Favorite init'); Background.init(); LoadingProgress.status('Background init'); Markers.init(); LoadingProgress.status('Markers init'); Notice$1.init(); LoadingProgress.status('Notice init'); Bell.init(); LoadingProgress.status('Bell init'); Menu$1.init(); LoadingProgress.status('Menu init'); Activity.init(); LoadingProgress.status('Activity init'); Screensaver$2.init(); LoadingProgress.status('Screensaver init'); Socket.init(); LoadingProgress.status('Socket init'); Account$1.init(); LoadingProgress.status('Account init'); Extensions.init(); LoadingProgress.status('Extensions init'); Plugins.init(); LoadingProgress.status('Plugins init'); Recomends.init(); LoadingProgress.status('Recomends init'); TimeTable.init(); LoadingProgress.status('Timetable init'); Helper$1.init(); LoadingProgress.status('Helper init'); Tizen.init(); LoadingProgress.status('Tizen init'); Player.init(); LoadingProgress.status('Player init'); Iframe.init(); LoadingProgress.status('Iframe init'); Parser.init(); LoadingProgress.status('Parser init'); WebOSLauncher.init(); LoadingProgress.status('WebOSLauncher init'); Theme$2.init(); LoadingProgress.status('Theme init'); AdManager.init(); LoadingProgress.status('AdManager init'); NavigationBar.init(); LoadingProgress.status('NavigationBar init'); Demo.init(); LoadingProgress.status('Demo init'); Speedtest.init(); LoadingProgress.status('Speedtest init'); Processing.init(); LoadingProgress.status('Processing init'); ParentalControl.init(); LoadingProgress.status('ParentalControl init'); Android.init(); LoadingProgress.status('Android init'); Sound$1.init(); LoadingProgress.status('Sound init'); Iptv.init(); LoadingProgress.status('Iptv init'); Logs.init(); LoadingProgress.status('Logs init'); Broadcast.init(); LoadingProgress.status('Broadcast init'); Search.init(); LoadingProgress.status('Search init'); DataBase.init(); LoadingProgress.status('DataBase init'); ContentRows.init(); LoadingProgress.status('ContentRows init'); // Добавляем источники поиска if (window.lampa_settings.account_use && !window.lampa_settings.disable_features.ai) Search.addSource(Ai$1.discovery()); LoadingProgress.status('Initialization successful'); // Выводим информацию о приложении var ratio = window.devicePixelRatio || 1; console.log('App', 'screen size:', Math.round(window.innerWidth * ratio) + ' / ' + Math.round(window.innerHeight * ratio)); console.log('App', 'interface size:', window.innerWidth + ' / ' + window.innerHeight); console.log('App', 'pixel ratio:', window.devicePixelRatio); console.log('App', 'user agent:', navigator.userAgent); console.log('App', 'touch points:', navigator.maxTouchPoints); console.log('App', 'is tv:', Platform.screen('tv')); console.log('App', 'is mobile:', Platform.screen('mobile')); console.log('App', 'is touch:', Utils$1.isTouchDevice()); console.log('App', 'is PWA:', Utils$1.isPWA()); console.log('App', 'platform:', Storage.get('platform', 'noname')); console.log('App', 'version:', object$2.app_version); console.log('App', 'build date:', '2026-10-04 19:56'); console.log('App', 'hash', '25ee3b0d1c845b4c71b87e7f3af98637'); console.log('App', 'location:', location.href); // Записываем uid if (!Storage.get('lampa_uid', '')) Storage.set('lampa_uid', Utils$1.uid()); // Ренедрим лампу Render.app(); LoadingProgress.status('Render app'); // Инициализируем остальные сервисы ServiceDeveloper.init(); LoadingProgress.status('ServiceDeveloper init'); ServiceTorserver.init(); LoadingProgress.status('ServiceTorserver init'); ServiceWatched.init(); LoadingProgress.status('ServiceWatched init'); ServiceSettings.init(); LoadingProgress.status('ServiceSettings init'); ServiceMetric.init(); LoadingProgress.status('ServiceMetric init'); ServiceRemoteFavorites.init(); LoadingProgress.status('ServiceRemoteFavorites init'); ServiceDMCA.init(); LoadingProgress.status('ServiceDMCA init'); ServiceFPS.init(); LoadingProgress.status('ServiceFPS init'); ServiceEvents.init(); LoadingProgress.status('ServiceEvents init'); ServiceLibs.init(); LoadingProgress.status('ServiceLibs init'); ServiceLGBT.init(); LoadingProgress.status('ServiceLGBT init'); ServiceChildren.init(); LoadingProgress.status('ServiceChildren init'); ServiceRemoteConfiguration.init(); LoadingProgress.status('ServiceRemoteConfiguration init'); // Обновляем слои Layer.update(); LoadingProgress.status('Layer update'); // Сообщаем о готовности LoadingProgress.status('Send app ready'); // Лампа полностью готова window.appready = true; window.app_time_end = Date.now(); Lampa.Listener.send('app', { type: 'ready' }); } /** * Показать приложение в любом случае */ function showApp() { var _this = this; LoadingProgress.status('Show app'); // Скрытие логотипа setTimeout(function () { if (window.show_app) return; window.show_app = true; LoadingProgress.destroy(); Keypad.enable(); Screensaver$2.enable(); $('.welcome').fadeOut(500, function () { $(_this).remove(); }); }, 1000); // Старт приложения startApp(); } /** * Приоритетная загрузка */ function loadTask() { Task.queue(function (next) { LoadingProgress.status('Open cache database'); Cache.openDatabase().then(function () { console.log('Cache', 'worked'); next(); })["catch"](function () { console.log('Cache', 'error', 'no open database'); next(); }); }); Task.queue(function (next) { LoadingProgress.status('Storage load reserve'); Storage.task(next); }); Task.queue(function (next) { LoadingProgress.status('Mirrors initialization'); LoadingProgress.step(2); Mirrors.task(next); }); Task.queue(function (next) { LoadingProgress.status('Plugins initialization'); LoadingProgress.step(3); Plugins.task(next); }); Task.queue(function (next) { LoadingProgress.status('Proxy initialization'); LoadingProgress.step(4); vpn.task(next); }); Task.queue(function (next) { LoadingProgress.status('Account initialization'); LoadingProgress.step(5); Account$1.task(next); }); Task.secondary(function () { setTimeout(showApp, 5000); }); // todo: потом удалить, это костыль для исправления старых адресов джакетта Task.secondary(function () { var tsl1 = window.localStorage.getItem('jackett_url') || ''; var tsl2 = window.localStorage.getItem('jackett_url_two') || ''; try { if (tsl1.toLowerCase() == 'jacred.xyz') { window.localStorage.setItem('jackett_url', 'jac.red'); } if (tsl2.toLowerCase() == 'jacred.xyz') { window.localStorage.setItem('jackett_url_two', 'jac.red'); } } catch (e) {} }); Task.secondary(function () { LoadingProgress.status('Loading plugins'); Plugins.load(showApp); }); Task.start(); } /** * Загрузка языка */ function loadLang() { var code = window.localStorage.getItem('language') || 'ru'; LoadingProgress.step(1); if (['ru', 'en'].indexOf(code) >= 0) loadTask();else { LoadingProgress.status('Loading language'); $.ajax({ url: (location.protocol == 'file:' || Platform.desktop() ? object$2.github_lampa : './') + 'lang/' + code + '.js', dataType: 'text', timeout: 10000, success: function success(data) { try { var translate = {}; eval((data + '').replace(/export default/g, 'translate = ').trim()); Lang.AddTranslation(code, translate); } catch (e) {} loadTask(); }, error: loadTask }); } } /** * Первая загрузка приложения */ function loadApp() { prepareApp(); // Готовим приложение // Если язык уже установлен, то запускаем приложение if (window.localStorage.getItem('language') || !window.lampa_settings.lang_use) { // Но сперва ожидаем не вызвали ли пользователь меню разработчика, затем подгружаем язык developerApp(loadLang); } else { // Иначе предлагаем выбрать язык LangChoice.open(function (code) { Storage.set('language', code, true); Storage.set('tmdb_lang', code, true); Keypad.disable(); loadLang(); }); Keypad.enable(); } } if (!window.fitst_load) { window.fitst_load = true; initClass(); if (navigator.userAgent.toLowerCase().indexOf('lampa_client') > -1) { var checkReady = function checkReady() { if (window.innerWidth > 0) loadApp();else { setTimeout(checkReady, 100); } }; checkReady(); } else loadApp(); } })();