Luxup

Outstream MoeVideo

Mar 30th, 2026 (edited)
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.92 KB | None | 0 0
  1. <div id="contentroll" style="position: relative;"></div>
  2. <script type="text/javascript">
  3. (() => {
  4. const VIDEO_CONTAINER_ID = 'contentroll';
  5.  
  6. const RSYA_VIEW_TIME_MS = 3500;
  7. const VIDEO_MAX_WAIT_MS = 3100;
  8. const VIDEO_CLOSE_ON_COMPLETE_TIMEOUT = 2000;
  9.  
  10. const VIDEO_PLACEMENT_ID = 12946;
  11. const VIDEO_ADVERT_COUNT = 30;
  12.  
  13. const STEP1_SITE_ID = 225780;
  14. const STEP1_BLOCK_ID = 721726;
  15.  
  16. const FALLBACK_SITE_ID = 225780;
  17. const FALLBACK_BLOCK_ID = 721724;
  18.  
  19. const videoContainer = document.querySelector('#'+VIDEO_CONTAINER_ID);
  20. if (!videoContainer) return;
  21.  
  22. const bannerWrapper = document.createElement('div');
  23. bannerWrapper.id = VIDEO_CONTAINER_ID + '_banner_wrapper';
  24. bannerWrapper.style.cssText = 'position: relative; width: 100%; z-index: 10; background: #fff; min-height: 0;';
  25.  
  26. videoContainer.parentNode.insertBefore(bannerWrapper, videoContainer);
  27.  
  28. videoContainer.style.cssText = 'position: absolute; top: 0; left: 0; width: 100%; height: 300px; z-index: -1; opacity: 0; pointer-events: none;';
  29.  
  30. let state = {
  31. bannerRendered: false,
  32. bannerEmpty: false,
  33. bannerViewed: false,
  34. videoLibLoaded: false,
  35. videoReady: false,
  36. videoShown: false,
  37. videoFinished: false,
  38. videoFailed: false,
  39. finalized: false
  40. };
  41.  
  42. const makeLogger = (tag = 'Waterfall') => {
  43. const prefix = "[" + tag + "] ->";
  44. return {
  45. log: (...args) => console.log(prefix, ...args),
  46. error: (...args) => console.error(prefix, ...args)
  47. };
  48. };
  49. const logger = makeLogger();
  50.  
  51. const enqueueLuxupBlock = (divId, siteId, blockId) => {
  52. (window['__lxGc__'] = window['__lxGc__'] || {'s':{},'b':0})['cmd'] = window['__lxGc__']['cmd'] || [];
  53. window['__lxGc__']['cmd'].push(function(){
  54. window['__lxGc__']['display'](divId, "_"+siteId, "_"+blockId);
  55. });
  56. };
  57.  
  58. const showFallback = (reason) => {
  59. if (state.finalized) return;
  60. state.finalized = true;
  61.  
  62. if (bannerWrapper.parentNode) bannerWrapper.parentNode.removeChild(bannerWrapper);
  63.  
  64. videoContainer.innerHTML = '';
  65. videoContainer.style.display = 'none';
  66.  
  67. logger.log("Шаг 3: Заглушка. Причина: "+reason);
  68.  
  69. const fallbackDiv = document.createElement('div');
  70. fallbackDiv.id = "lx_" + FALLBACK_BLOCK_ID + "_wrapper";
  71.  
  72. if (videoContainer.parentNode) {
  73. videoContainer.parentNode.insertBefore(fallbackDiv, videoContainer);
  74. }
  75. enqueueLuxupBlock(fallbackDiv.id, FALLBACK_SITE_ID, FALLBACK_BLOCK_ID);
  76. };
  77.  
  78. const showVideo = () => {
  79. if (state.finalized || state.videoShown) return;
  80. state.videoShown = true;
  81.  
  82. logger.log('Успех! Снимаем слой баннера, показываем видео');
  83.  
  84. if (bannerWrapper.parentNode) bannerWrapper.parentNode.removeChild(bannerWrapper);
  85.  
  86. videoContainer.style.position = 'relative';
  87. videoContainer.style.zIndex = '100';
  88. videoContainer.style.opacity = '1';
  89. videoContainer.style.height = 'auto';
  90. videoContainer.style.pointerEvents = 'auto';
  91. };
  92.  
  93. const checkStatus = () => {
  94. if (state.bannerEmpty) {
  95. showFallback('Баннер пуст');
  96. return;
  97. }
  98. if (!state.bannerViewed) return;
  99. if (state.videoReady) {
  100. showVideo();
  101. }
  102. };
  103.  
  104. const prepareVideo = () => {
  105. var closeOnCompleteTimeout = null;
  106.  
  107. logger.log("Запускаем инициализацию moevideo в скрытом контейнере...");
  108.  
  109. window['addContentRoll']({
  110. element: '#'+VIDEO_CONTAINER_ID,
  111. width: '100%',
  112. placement: VIDEO_PLACEMENT_ID,
  113. promo: true,
  114. advertCount: VIDEO_ADVERT_COUNT,
  115. slot: 'page',
  116. sound: 'onclick',
  117. deviceMode: 'all',
  118. background: 'none',
  119. fly: {mode: 'off'},
  120.  
  121. playerLoad: function() { logger.log("addContentRoll событие: PlayerLoad"); },
  122. playerClose: function() {
  123. logger.log("addContentRoll событие: PlayerClose");
  124. state.videoFailed = true;
  125. if (state.videoShown) showFallback('Видео закрыто');
  126. },
  127. adLoad: function() { logger.log("addContentRoll событие: AdLoaded"); },
  128. adStart: function() {
  129. logger.log("addContentRoll событие: AdStarted -> ВИДЕО ГОТОВО");
  130. state.videoReady = true;
  131. if (closeOnCompleteTimeout) { clearTimeout(closeOnCompleteTimeout); closeOnCompleteTimeout = null; }
  132. checkStatus();
  133. },
  134. adImp: function() { logger.log("addContentRoll событие: AdImpression"); },
  135. ad25: function() { logger.log("addContentRoll событие: AdVideoFirstQuartile"); },
  136. ad50: function() { logger.log("addContentRoll событие: AdVideoMidpoint"); },
  137. ad75: function() { logger.log("addContentRoll событие: AdVideoThirdQuartile"); },
  138.  
  139. adCompl: function() {
  140. logger.log("addContentRoll событие: AdVideoComplete");
  141. closeOnCompleteTimeout = setTimeout(() => {
  142. logger.log("addContentRoll таймаут завершения видео");
  143. state.videoFinished = true;
  144. if (state.videoShown) {
  145. showFallback('Видео завершено (таймаут)');
  146. }
  147. }, VIDEO_CLOSE_ON_COMPLETE_TIMEOUT);
  148. },
  149.  
  150. adFinish: function() {
  151. logger.log("addContentRoll событие: AdStopped");
  152. state.videoFinished = true;
  153. if (closeOnCompleteTimeout) { clearTimeout(closeOnCompleteTimeout); closeOnCompleteTimeout = null; }
  154. if (state.videoShown) {
  155. showFallback('Видео завершено (AdStopped)');
  156. }
  157. },
  158.  
  159. adError: function() {
  160. logger.log("addContentRoll событие: AdError");
  161. state.videoFailed = true;
  162. if (state.videoShown) showFallback('Ошибка видео');
  163. },
  164. adEmpty: function() {
  165. logger.log("addContentRoll событие: AdEmpty");
  166. state.videoFailed = true;
  167. if (state.videoShown) showFallback('Видео пустое');
  168. }
  169. });
  170. };
  171.  
  172. const runBannerStep = () => {
  173. logger.log('Поток 1: Старт баннера');
  174.  
  175. const bannerDiv = document.createElement('div');
  176. bannerDiv.id = "lx_" + STEP1_BLOCK_ID;
  177. bannerWrapper.appendChild(bannerDiv);
  178.  
  179. enqueueLuxupBlock(bannerDiv.id, STEP1_SITE_ID, STEP1_BLOCK_ID);
  180.  
  181. let accumulatedTime = 0;
  182. let enterTimestamp = 0;
  183. let finishTimeout = null;
  184.  
  185. const bannerViewedCallback = () => {
  186. if (state.finalized) return;
  187. logger.log(" Баннер просмотрен (" + RSYA_VIEW_TIME_MS + "ms)");
  188. state.bannerViewed = true;
  189. if (finishTimeout) { clearTimeout(finishTimeout); finishTimeout = null; }
  190. viewedObserver.disconnect();
  191. checkStatus();
  192. };
  193.  
  194. const viewedObserver = new IntersectionObserver((entries) => {
  195. const isIntersecting = entries[0].isIntersecting;
  196. if (isIntersecting && bannerWrapper.isConnected && !state.finalized) {
  197. logger.log(" Баннер видим");
  198. if (!enterTimestamp) {
  199. enterTimestamp = Date.now();
  200. const remaining = RSYA_VIEW_TIME_MS-accumulatedTime;
  201. if (remaining <= 0) bannerViewedCallback();
  202. else finishTimeout = setTimeout(() => {
  203. if (!state.finalized) {
  204. if (enterTimestamp) { accumulatedTime += Date.now()-enterTimestamp; enterTimestamp = 0; }
  205. if (accumulatedTime >= RSYA_VIEW_TIME_MS) bannerViewedCallback();
  206. }
  207. }, remaining);
  208. }
  209. } else {
  210. logger.log(" Баннер не видим");
  211. if (enterTimestamp) { accumulatedTime += Date.now()-enterTimestamp; enterTimestamp = 0; }
  212. if (finishTimeout) { clearTimeout(finishTimeout); finishTimeout = null; }
  213. }
  214. }, {threshold: 0.5});
  215.  
  216. const bannerReadyObserver = new MutationObserver(() => {
  217. if (state.finalized) { bannerReadyObserver.disconnect(); return; }
  218. if (!state.bannerRendered && bannerWrapper.querySelector('.__lxG__not_empty_block')) {
  219. state.bannerRendered = true;
  220. logger.log('Баннер загружен, запускаем Observer просмотра');
  221. viewedObserver.observe(bannerWrapper);
  222. }
  223. if (!state.bannerEmpty && bannerWrapper.querySelector('.__lxG__rendered_none')) {
  224. state.bannerEmpty = true;
  225. logger.log('Баннер пустой');
  226. bannerReadyObserver.disconnect();
  227. checkStatus();
  228. }
  229. });
  230. bannerReadyObserver.observe(bannerWrapper, { childList: true, subtree: true, attributes: true });
  231.  
  232. if (bannerWrapper.querySelector('.__lxG__not_empty_block')) {
  233. state.bannerRendered = true;
  234. viewedObserver.observe(bannerWrapper);
  235. }
  236. };
  237.  
  238. const runVideoStep = () => {
  239. logger.log('Поток 2: Старт видео (фон)');
  240. const s = document.createElement("script");
  241. s.src = "https://cdn1.moe.video/p/cr.js";
  242. s.async = true;
  243.  
  244. setTimeout(() => {
  245. if (!state.finalized && !state.videoReady) {
  246. state.videoFailed = true;
  247. if (state.bannerViewed) logger.log('Глобальный таймаут видео (но заглушку не показываем, висит баннер)');
  248. }
  249. }, VIDEO_MAX_WAIT_MS);
  250.  
  251. s.onload = () => {
  252. state.videoLibLoaded = true;
  253. logger.log("moe.video загружен");
  254. prepareVideo();
  255. };
  256. s.onerror = () => {
  257. logger.log("moe.video ошибка загрузки");
  258. state.videoFailed = true;
  259. };
  260. document.head.appendChild(s);
  261. };
  262.  
  263. runBannerStep();
  264. runVideoStep();
  265. })();
  266. </script>
Advertisement
Add Comment
Please, Sign In to add comment