Nejlevnější cesta k HBO vede přes SledováníTV: tuhle IPTV si pustíte úplně všude a je i ve Full HD (vyzkoušeno)

22. 1. 2018

Sdílet

 Autor: Radomír Kejduš

Pokračujeme v seriálu o internetových televizích. SledováníTV patří mezi nejvíce podporované IPTV a kromě vlastního set-top-boxu mají aplikaci pro Android, nově i pro Android TV, LG WebOS, My Home Screen (Panasonic) a Samsung Tizen. A samozřejmě nechybí mobilní a webová aplikace. Neexistuje tak téměř systém, kde byste si tuhle televizi nepustili.

S takovou podporou žádnou jinou IPTV nenajdete. Pokud máte doma více platforem, na kterých chcete TV sledovat, SledováníTV je pravděpodobně bude umět všechny, a to je jeho hlavní výhoda. Pro HD vysílání stačí internet o rychlosti 2 Mb/s díky H.265, ale to už začíná být standardem u většiny IPTV. Některé stanice už vysílá dokonce ve Full HD (stačí datový tok asi 2,4 Mb/s). Zatímco konkurence většinou využívá nangu.TV, tak SledováníTV běží na vlastní platformě. Existuje už pět let a od roku 2016 funguje na sítích všech poskytovatelů internetu.

sledovanitv-merunka Set-top-box SledováníTV

Napřímo, nebo přes partnery

Provozovatelem SledováníTV je brněnská společnost, která službu nabízí primárně přes partnery. Je to nezvyklé a můžete se stát i zákazníkem napřímo, ale pokud jste v síti některého z asi 500 poskytovatelů internetu, tedy partnerů, dostanete většinou lepší nabídku. Ty se mohou různit, každý partner si může balíčky poskládat podle sebe. Většinou jsou ale podobné.

Přímo přes SledováníTV jsou v nabídce tři tarify: Základ (199 Kč), Standard a Premium. Liší se nejen počtem TV stanic, ale i délkou zpětného zhlédnutí a kapacitou pro uložení nahrávek. Nejvyšší tarif pak má v ceně i HBO Go.

screenshot-2017-11-19-12-08-50

Délka zpětného zhlédnutí i úložiště pro nahrávky se mění spolu s tarifem. V základu je k dispozici 50 hodin zpět a 25 hodin nahrávek, tarif Standard nabízí už týden a 50 hodin nahrávek a Premium zvyšuje úložiště na 120 hodin. Nahrávky budete mít uložené rekordní tři měsíce, a tuhle lhůtu lze dokonce prodloužit pouhým kliknutím.

O2 a T-Mobile provozují vlastní televizi, na SledováníTV vsadil Vodafone

U partnerů se nabídka liší. Zpětné zhlédnutí bývá většinou týden a k tomu dostanete 50 hodin úložiště. Mezi mnoha malými a lokálními poskytovateli ale najdete třeba Vodafone, který narozdíl od O2 a T-Mobilu zvolil spolupráci se SledováníTV místo budování vlastní služby. Obecně i při zakoupení služby u nějakého partnera nejste vázáni jen na jeho síť a televizní stanice (snad až na Eurosport) můžete sledovat i jinde. Všichni nabízejí televizi jako svou službu a mohou si k ní dát i svoje logo. Osobně jsem právě v síti Vodafonu, takže mohu porovnávat jeho nabídky.

Brněnská centrála SledováníTV. Tady vyvíjení aplikace, ale servery s uloženými pořady jsou v Praze

photo_2018-01-18_15-57-28 Vývojáři mají k dispozici různé přehrávače i televize, na kterých aplikace testují

Ve zmíněném Full HD vysílají například stanice ČT (bez ČT24), HBO, Nova, Prima či Eurosport. Konečně budete mít pěkný obraz běžného televizního vysílání i na drahém 4K televizoru. Obecně vysílat ve Full HD by nebyl problém ani pro ostatní IPTV, ale zatím tuto možnost z více důvodů nenabídli.

Bez bulváru a s HBO

Zrovna u Vodafonu můžete mít i docela specifický a zajímavý balíček, který za 255 Kč měsíčně obsahuje jenom kanály České televize a HBO, nic jiného. Být k tomu i nějaký prémiový sportovní kanál, není co řešit. Vzhledem k tomu, že opravdu pořádné sportovní kanály má exkluzivně jen Digi TV a O2 TV a ČT4 Sport v tomto balíčku nechybí, je i tak tento tarif skoro nepřekonatelný.

screenshot-2018-01-04-19-02-48 Jedna z nabídek Vodafonu

Nabízí jej ale jen Vodafone. Pokud by vám přeci jen nějaké ty barrandovy chyběly, lze přikoupit balíček s názvem Pro všechny za 59 Kč a k tomu získat i běžné kanály. Podobnou nabídku má sice i Digi TV (Sport Plus za 349 Kč), ale bez možnosti ji spárovat s HBO nebo s jiným kanálem.

Vodafone nabízí ještě balíčky Pro rodinu za 189 Kč, Pro všechny za 59 Kč, Pro sportovce za 115 Kč (ČT a sportovní kanály). Sportovními kanály je myšleno Sport 1, Sport 2, Nova Sport, ČT4 Sport, Arena Sport či Golf Channel.

U SledováníTV jsou v nabídce téměř všech balíčků i „stanice“ nazvané Akvárium a Krb a je myslím jasné, co na nich uvidíte. Musím přiznat, že jsem na ně koukal většinu času nejradši a jejich provedení je opravdu hezké.

img_6545 img_6544

Kolik televizorů doma máš?

Na rozdíl od konkurenčních řešení si u SledováníTV nemusíte připlácet za více televizorů v domácnosti. Celkem totiž můžete mít až čtyři velké televize a je jen na vás, zda půjde o Smart TV, Apple TV nebo set-top-box (který si musíte koupit za zhruba 2200 Kč), ale díky podpoře si místo něj můžete koupit skoro jakýkoliv HD přehrávač s Androidem. Cenově to ale nevyjde o tolik lépe a místo aplikace pro Set-top-box (tu si sami nestáhnete) budete mít rozhraní aplikace pro Android.

img_6539 Úvodní obrazovka rozhraní na set-top-boxu

img_6543 img_6542  img_6536 img_6533

Jak benevolentní je tato služba u velkých televizí, tak u mobilních je zase poměrně striktní. Respektive celkem můžete mít čtyři zařízení a i když budete mít jen jednu velkou televizi, tak zbývají jen tři sloty. Například jedno webové rozhraní, dva mobily a konec. Ale konkurence je na tom podobně, většinou také dovoluje čtyři zařízení, ovšem jen jedno z nich může být televizor.

img_6576 Set-top-box lze nahradit téměř jakýmkoliv zařízením s Androidem, do kterého si nainstalujete příslušnou aplikaci.

Vlastní set-top-box je malinká krabička s Androidem a jednoduchou nadstavbou. Na ploše máte ikonku aplikace SledováníTV a možnost přidat další. Už v základu vám nabídne k doinstalování Kodi a YouTube, nabídka aplikací je schválně omezená a Google Play zde není. Set-top-box zvládne přehrát i náročná 4K videa z domácí sítě.

img_6575 Set-top-box

Dálkový ovladač je pohodlný, směrový, ale některá tlačítka mají v aplikaci SledováníTV jinou funkci, než autoři zamýšleli. Přeci jen jde o standardní krabičku s Androidem a od tvůrců aplikace by si zasloužila lepší namapování. Třeba tlačítka pro posun po časové ose listují kanály a některá tlačítka nemají žádnou funkci. Ale vývojáři slibují nápravu.

img_6553 Úvodní obrazovka nové aplikace pro Android TV

Aplikace pro Android TV, kterou spustíte na chytrých televizích od Sony nebo Philipsu a na zařízení Shield TV je trochu jiná, a podle ní by se v budoucnu měly změnit i ostatní aplikace.

img_6557 img_6554  img_6549 img_6548

Webové rozhraní a smartphone

Webové rozhraní je jednoduché a přehledné. Pod obrazem lze ručně přepínat kvalitu mezi HD a SD a mezi přehráváním pomocí Flashe a HTML5. Jsou tu funkce pro ztmavení okolí nebo přesunu obrazu do samostatného okna.

screenshot-2018-01-04-15-29-19

Vpravo máte seznam kanálů a aktuálně vysílaný pořad, pod obrazem pak pořady aktuální stanice.

screenshot-2017-11-19-12-21-07 Webové rozhraní

Seznam stanic lze filtrovat podle jejich typu – třeba dokumentární nebo dětské. Ale více bych ocenil možnost vlastní skupiny stanic. Mnoho televizních stanic se svou kvalitou hodí tak maximálně k odinstalování, a zde je nemůžu ani skrýt. Můžete si ale celkem snadno měnit pořadí a ty lepší stanice dát dopředu. Pokud k nahrávání zvolíte místo pořadu nějaký seriál, můžete automaticky nechat nahrávat všechny následující díly.

screenshot-2018-01-04-15-29-59

screenshot-2017-11-19-12-27-46

Pro přístup k EPG musíte vysílání opustit, nabídka navíc více připomíná televizní program jako z novin než „tajmlajnu“ vysílání. Oproti tomu na mobilu je EPG provedeno pěkně.

screenshot-2017-11-19-12-30-54 Stejně jako u Kuki lze pro sledování využít aplikaci VLC Player

Sledovat televizi lze i přes aplikaci VLC player podobně jako u Kuki. Obecně je webové rozhraní až na EPG vcelku dobré a lepší jsem zaznamenal jen u Digi2GO.

screenshot-2018-01-04-15-29-53 Současná podoba EPG. Mělo by se změnit

Velmi dobrá je i aplikace pro mobily (s Androidem), která kromě jiného umí otočit obrazovku i v případě, kdy to nemáte systémově povolené. Je to jednoduchá funkce, kterou ale mnoho aplikací ignoruje. Přesto bych ji ještě vylepšil a otočení v tom případě nechal na tlačítku podobně jako u aplikace Youtube.

screenshot_20180104-175634 screenshot_20180104-175607 screenshot_20180104-175552 screenshot_20180104-175533

Hodnocení

SledováníTV vyniká počtem podporovaných platforem. Netradičně prodává službu spíše přes partnery – pokud jste v síti některého z asi 500 poskytovatelů internetu (včetně Vodafonu), ale lze se stát zákazníkem i napřímo. Ceny přes partnery bývají výhodnější, jelikož nabídku IPTV ve své síti berou i jako konkurenční výhodu a někteří občas i dotují.

Důležitá funkce zpětného zhlédnutí je u SledováníTV dlouhá až týden, což je ale dnes už skoro standard. Lépe je na tom jen LepšíTV, hůře zase naopak Kuki od Netboxu. Možnost délky záznamu nahraných pořadů je také poměrně standardní a liší se podle balíčku. Ovšem uložený pořad bude dostupný rekordní tři měsíce.

Aplikace by ještě potřebovaly na některých platformách doladit a brzy by měly všechny vypadat podobně jako nová aplikace pro Android TV. Třeba současnou podobu EPG na webovém rozhraní berou jako slabinu i vývojáři.

SledováníTV se hodí hlavně do domácností s více televizory, protože nejste tolik omezeni a klidně můžete zapojit čtyři velké televize. Boduje i cenou, ale prémiové sportovní kanály nenabízí, ty zůstávají doménou pouze Digi TV a O2 TV. Ty běžnější sportovní kanály zde samozřejmě najdete. SledováníTV u Vodafonu láká na skvělý balíček obsahujícím kanály ČT a HBO bez dalšího bulvárního balastu a za příjemnou cenu. Navíc už vysílá některé kanály ve Full HD, což sice nikde neinzeruje, ale pokud máte například lepší 4K televizi, rozdíl poznáte.

cnews_smartbuy

hacking_tip

 

partnerem rubriky IPTV je společnost CETIN a ZrychlujemeČesko.cz

Autor článku

Redaktor portálu Cnews.cz. Zaměřuje se na televizní témata, technologické zpravodajství, mobilní operátory a vědu. Příznivec kompaktních smartphonů. Profil autora →

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »