405 lines
17 KiB
HTML
405 lines
17 KiB
HTML
|
|
<!DOCTYPE html>
|
|||
|
|
<html>
|
|||
|
|
<head>
|
|||
|
|
<title>Vast-Player Example</title>
|
|||
|
|
<script src="https://cdn.jsdelivr.net/npm/vast-player@latest/dist/vast-player.min.js"></script>
|
|||
|
|
<style>
|
|||
|
|
body {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 0;
|
|||
|
|
background: #000;
|
|||
|
|
font-family: Arial, sans-serif;
|
|||
|
|
}
|
|||
|
|
#container {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100vh;
|
|||
|
|
position: relative;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
.loading {
|
|||
|
|
color: white;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
.error {
|
|||
|
|
color: #ff6b6b;
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 20px;
|
|||
|
|
}
|
|||
|
|
.ad-info {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 10px;
|
|||
|
|
left: 10px;
|
|||
|
|
color: white;
|
|||
|
|
background: rgba(0,0,0,0.7);
|
|||
|
|
padding: 10px;
|
|||
|
|
border-radius: 5px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
z-index: 1000;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.skip-button {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 10px;
|
|||
|
|
right: 10px;
|
|||
|
|
background: rgba(0,0,0,0.8);
|
|||
|
|
color: white;
|
|||
|
|
border: 2px solid white;
|
|||
|
|
border-radius: 20px;
|
|||
|
|
padding: 8px 16px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
cursor: pointer;
|
|||
|
|
z-index: 1001;
|
|||
|
|
display: none;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.skip-button:hover {
|
|||
|
|
background: rgba(255,255,255,0.2);
|
|||
|
|
transform: scale(1.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.skip-button.visible {
|
|||
|
|
display: block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.skip-timer {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 10px;
|
|||
|
|
right: 10px;
|
|||
|
|
background: rgba(0,0,0,0.8);
|
|||
|
|
color: white;
|
|||
|
|
border: 2px solid #ff6b6b;
|
|||
|
|
border-radius: 20px;
|
|||
|
|
padding: 8px 16px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
z-index: 1001;
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.skip-timer.visible {
|
|||
|
|
display: block;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<div id="container">
|
|||
|
|
<div class="loading">Загрузка рекламы...</div>
|
|||
|
|
<button id="skipButton" class="skip-button" onclick="skipAd()">Пропустить рекламу</button>
|
|||
|
|
<div id="skipTimer" class="skip-timer">Пропустить через: <span id="skipTime">5</span>с</div>
|
|||
|
|
</div>
|
|||
|
|
<script>
|
|||
|
|
(function(VASTPlayer) {
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
var container = document.getElementById('container');
|
|||
|
|
var player = new VASTPlayer(container);
|
|||
|
|
|
|||
|
|
// Элементы для кнопки пропуска
|
|||
|
|
var skipButton = document.getElementById('skipButton');
|
|||
|
|
var skipTimer = document.getElementById('skipTimer');
|
|||
|
|
var skipTimeSpan = document.getElementById('skipTime');
|
|||
|
|
var skipTimerInterval = null;
|
|||
|
|
var skipOffset = 5; // секунд до возможности пропуска (по умолчанию)
|
|||
|
|
var skipOffsetFromVast = null; // будет установлено из VAST данных
|
|||
|
|
|
|||
|
|
// Функция для отправки событий в Flutter
|
|||
|
|
function sendToFlutter(eventName, data) {
|
|||
|
|
console.log('Sending to Flutter:', eventName, data);
|
|||
|
|
if (window.flutter_inappwebview) {
|
|||
|
|
window.flutter_inappwebview.callHandler(eventName, data);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для парсинга времени из формата HH:MM:SS
|
|||
|
|
function parseTimeToSeconds(timeString) {
|
|||
|
|
if (!timeString) return 5; // по умолчанию 5 секунд
|
|||
|
|
|
|||
|
|
var parts = timeString.split(':');
|
|||
|
|
if (parts.length === 3) {
|
|||
|
|
var hours = parseInt(parts[0]) || 0;
|
|||
|
|
var minutes = parseInt(parts[1]) || 0;
|
|||
|
|
var seconds = parseInt(parts[2]) || 0;
|
|||
|
|
return hours * 3600 + minutes * 60 + seconds;
|
|||
|
|
}
|
|||
|
|
return 5; // по умолчанию 5 секунд
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для получения skipoffset из VAST данных
|
|||
|
|
function getSkipOffsetFromVast() {
|
|||
|
|
try {
|
|||
|
|
// Пытаемся получить skipoffset из VAST данных
|
|||
|
|
if (player && player.vastResponse && player.vastResponse.ads && player.vastResponse.ads.length > 0) {
|
|||
|
|
var ad = player.vastResponse.ads[0];
|
|||
|
|
if (ad.inLine && ad.inLine.creatives && ad.inLine.creatives.length > 0) {
|
|||
|
|
var creative = ad.inLine.creatives[0];
|
|||
|
|
if (creative.linear && creative.linear.skipoffset) {
|
|||
|
|
return parseTimeToSeconds(creative.linear.skipoffset);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Альтернативный способ - парсинг XML
|
|||
|
|
if (player && player.vastResponse && player.vastResponse.xml) {
|
|||
|
|
var xmlDoc = new DOMParser().parseFromString(player.vastResponse.xml, "text/xml");
|
|||
|
|
var linearElement = xmlDoc.querySelector("Linear");
|
|||
|
|
if (linearElement && linearElement.getAttribute("skipoffset")) {
|
|||
|
|
var skipoffset = linearElement.getAttribute("skipoffset");
|
|||
|
|
console.log('Found skipoffset in XML:', skipoffset);
|
|||
|
|
return parseTimeToSeconds(skipoffset);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
console.log('Error parsing VAST skipoffset:', e);
|
|||
|
|
}
|
|||
|
|
return skipOffset; // возвращаем значение по умолчанию
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для пропуска рекламы
|
|||
|
|
window.skipAd = function() {
|
|||
|
|
console.log('Skipping ad');
|
|||
|
|
player.skipAd();
|
|||
|
|
hideSkipElements();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Функция для показа кнопки пропуска
|
|||
|
|
function showSkipButton() {
|
|||
|
|
console.log('Showing skip button');
|
|||
|
|
skipButton.classList.add('visible');
|
|||
|
|
skipTimer.classList.remove('visible');
|
|||
|
|
if (skipTimerInterval) {
|
|||
|
|
clearInterval(skipTimerInterval);
|
|||
|
|
skipTimerInterval = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для показа таймера пропуска
|
|||
|
|
function showSkipTimer() {
|
|||
|
|
console.log('Showing skip timer with offset:', skipOffsetFromVast);
|
|||
|
|
skipTimer.classList.add('visible');
|
|||
|
|
skipButton.classList.remove('visible');
|
|||
|
|
|
|||
|
|
var timeLeft = skipOffsetFromVast;
|
|||
|
|
skipTimeSpan.textContent = timeLeft;
|
|||
|
|
|
|||
|
|
skipTimerInterval = setInterval(function() {
|
|||
|
|
timeLeft--;
|
|||
|
|
skipTimeSpan.textContent = timeLeft;
|
|||
|
|
|
|||
|
|
if (timeLeft <= 0) {
|
|||
|
|
showSkipButton();
|
|||
|
|
}
|
|||
|
|
}, 1000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для скрытия элементов пропуска
|
|||
|
|
function hideSkipElements() {
|
|||
|
|
console.log('Hiding skip elements');
|
|||
|
|
skipButton.classList.remove('visible');
|
|||
|
|
skipTimer.classList.remove('visible');
|
|||
|
|
if (skipTimerInterval) {
|
|||
|
|
clearInterval(skipTimerInterval);
|
|||
|
|
skipTimerInterval = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для загрузки рекламы с URL
|
|||
|
|
window.loadVastAd = function(vastUrl) {
|
|||
|
|
console.log('Loading VAST ad from URL:', vastUrl);
|
|||
|
|
container.innerHTML = '<div class="loading">Загрузка рекламы...</div>';
|
|||
|
|
hideSkipElements(); // Скрываем элементы пропуска при загрузке
|
|||
|
|
|
|||
|
|
// Восстанавливаем элементы пропуска
|
|||
|
|
container.innerHTML += '<button id="skipButton" class="skip-button" onclick="skipAd()">Пропустить рекламу</button>';
|
|||
|
|
container.innerHTML += '<div id="skipTimer" class="skip-timer">Пропустить через: <span id="skipTime">5</span>с</div>';
|
|||
|
|
|
|||
|
|
// Обновляем ссылки на элементы
|
|||
|
|
skipButton = document.getElementById('skipButton');
|
|||
|
|
skipTimer = document.getElementById('skipTimer');
|
|||
|
|
skipTimeSpan = document.getElementById('skipTime');
|
|||
|
|
|
|||
|
|
console.log('Skip elements restored:', {
|
|||
|
|
skipButton: skipButton,
|
|||
|
|
skipTimer: skipTimer,
|
|||
|
|
skipTimeSpan: skipTimeSpan
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
player.load(vastUrl).then(function() {
|
|||
|
|
console.log('VAST ad loaded successfully');
|
|||
|
|
sendToFlutter('onAdLoaded');
|
|||
|
|
return player.startAd();
|
|||
|
|
}).catch(function(reason) {
|
|||
|
|
console.error('Failed to load ad:', reason);
|
|||
|
|
container.innerHTML = '<div class="error">Ошибка загрузки рекламы: ' + reason.message + '</div>';
|
|||
|
|
sendToFlutter('onAdError', reason);
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Функция для загрузки пользовательского XML
|
|||
|
|
window.loadCustomXml = function(xmlContent) {
|
|||
|
|
console.log('Loading custom XML ad, content length:', xmlContent.length);
|
|||
|
|
container.innerHTML = '<div class="loading">Загрузка пользовательской рекламы...</div>';
|
|||
|
|
hideSkipElements(); // Скрываем элементы пропуска при загрузке
|
|||
|
|
|
|||
|
|
// Восстанавливаем элементы пропуска
|
|||
|
|
container.innerHTML += '<button id="skipButton" class="skip-button" onclick="skipAd()">Пропустить рекламу</button>';
|
|||
|
|
container.innerHTML += '<div id="skipTimer" class="skip-timer">Пропустить через: <span id="skipTime">5</span>с</div>';
|
|||
|
|
|
|||
|
|
// Обновляем ссылки на элементы
|
|||
|
|
skipButton = document.getElementById('skipButton');
|
|||
|
|
skipTimer = document.getElementById('skipTimer');
|
|||
|
|
skipTimeSpan = document.getElementById('skipTime');
|
|||
|
|
|
|||
|
|
console.log('Skip elements restored for custom XML:', {
|
|||
|
|
skipButton: skipButton,
|
|||
|
|
skipTimer: skipTimer,
|
|||
|
|
skipTimeSpan: skipTimeSpan
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// Создаем Blob URL для пользовательского XML
|
|||
|
|
var blob = new Blob([xmlContent], {type: 'application/xml'});
|
|||
|
|
var url = URL.createObjectURL(blob);
|
|||
|
|
console.log('Custom XML blob URL created:', url);
|
|||
|
|
|
|||
|
|
player.load(url).then(function() {
|
|||
|
|
console.log('Custom XML ad loaded successfully');
|
|||
|
|
sendToFlutter('onAdLoaded');
|
|||
|
|
return player.startAd();
|
|||
|
|
}).catch(function(reason) {
|
|||
|
|
console.error('Failed to load custom ad:', reason);
|
|||
|
|
container.innerHTML = '<div class="error">Ошибка загрузки пользовательской рекламы: ' + reason.message + '</div>';
|
|||
|
|
sendToFlutter('onAdError', reason);
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Error creating blob URL for custom XML:', error);
|
|||
|
|
container.innerHTML = '<div class="error">Ошибка создания пользовательской рекламы: ' + error.message + '</div>';
|
|||
|
|
sendToFlutter('onAdError', error);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Функция для остановки рекламы
|
|||
|
|
window.stopAd = function() {
|
|||
|
|
console.log('Stopping ad');
|
|||
|
|
player.stopAd();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Функция для паузы/возобновления рекламы
|
|||
|
|
window.toggleAdPlayback = function() {
|
|||
|
|
if (player.isPlaying()) {
|
|||
|
|
player.pauseAd();
|
|||
|
|
} else {
|
|||
|
|
player.resumeAd();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Обработчики всех событий
|
|||
|
|
[
|
|||
|
|
'AdLoaded',
|
|||
|
|
'AdStarted',
|
|||
|
|
'AdStopped',
|
|||
|
|
'AdSkipped',
|
|||
|
|
'AdSkippableStateChange',
|
|||
|
|
'AdSizeChange',
|
|||
|
|
'AdLinearChange',
|
|||
|
|
'AdDurationChange',
|
|||
|
|
'AdExpandedChange',
|
|||
|
|
'AdRemainingTimeChange',
|
|||
|
|
'AdVolumeChange',
|
|||
|
|
'AdImpression',
|
|||
|
|
'AdVideoStart',
|
|||
|
|
'AdVideoFirstQuartile',
|
|||
|
|
'AdVideoMidpoint',
|
|||
|
|
'AdVideoThirdQuartile',
|
|||
|
|
'AdVideoComplete',
|
|||
|
|
'AdClickThru',
|
|||
|
|
'AdInteraction',
|
|||
|
|
'AdUserAcceptInvitation',
|
|||
|
|
'AdUserMinimize',
|
|||
|
|
'AdUserClose',
|
|||
|
|
'AdPaused',
|
|||
|
|
'AdPlaying',
|
|||
|
|
'AdLog',
|
|||
|
|
'AdError'
|
|||
|
|
].forEach(function(type) {
|
|||
|
|
player.on(type, function() {
|
|||
|
|
var args = Array.prototype.slice.call(arguments);
|
|||
|
|
console.log('EVENT: ' + type, args);
|
|||
|
|
|
|||
|
|
// Отправляем событие в Flutter
|
|||
|
|
var eventName = 'on' + type;
|
|||
|
|
sendToFlutter(eventName, args.length > 0 ? args[0] : null);
|
|||
|
|
|
|||
|
|
// Специальная обработка для завершения рекламы
|
|||
|
|
if (type === 'AdStopped') {
|
|||
|
|
sendToFlutter('onAdComplete');
|
|||
|
|
hideSkipElements();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Специальная обработка для пропуска рекламы
|
|||
|
|
if (type === 'AdSkipped') {
|
|||
|
|
sendToFlutter('onAdSkipped');
|
|||
|
|
hideSkipElements();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Специальная обработка для изменения состояния пропуска
|
|||
|
|
if (type === 'AdSkippableStateChange') {
|
|||
|
|
if (args.length > 0 && args[0]) {
|
|||
|
|
// Получаем skipoffset из VAST данных
|
|||
|
|
skipOffsetFromVast = getSkipOffsetFromVast();
|
|||
|
|
console.log('Ad is skippable, skip offset from VAST:', skipOffsetFromVast);
|
|||
|
|
showSkipTimer(); // Показываем таймер, если реклама стала пропускаемой
|
|||
|
|
} else {
|
|||
|
|
hideSkipElements(); // Скрываем, если реклама не пропускаемая
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Специальная обработка для начала рекламы
|
|||
|
|
if (type === 'AdStarted') {
|
|||
|
|
sendToFlutter('onAdStarted');
|
|||
|
|
// Проверяем, пропускаемая ли реклама
|
|||
|
|
if (player.isSkippable()) {
|
|||
|
|
skipOffsetFromVast = getSkipOffsetFromVast();
|
|||
|
|
console.log('Ad started and is skippable, skip offset:', skipOffsetFromVast);
|
|||
|
|
showSkipTimer();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Специальная обработка для загрузки рекламы
|
|||
|
|
if (type === 'AdLoaded') {
|
|||
|
|
sendToFlutter('onAdLoaded');
|
|||
|
|
// Проверяем skipoffset сразу после загрузки
|
|||
|
|
setTimeout(function() {
|
|||
|
|
if (player.isSkippable()) {
|
|||
|
|
skipOffsetFromVast = getSkipOffsetFromVast();
|
|||
|
|
console.log('Ad loaded and is skippable, skip offset:', skipOffsetFromVast);
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Специальная обработка для кликов
|
|||
|
|
if (type === 'AdClickThru') {
|
|||
|
|
sendToFlutter('onAdClick');
|
|||
|
|
// Пытаемся открыть ссылку
|
|||
|
|
if (args.length > 0 && args[0]) {
|
|||
|
|
console.log('Opening click URL:', args[0]);
|
|||
|
|
try {
|
|||
|
|
window.open(args[0], '_blank');
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('Error opening click URL:', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Загружаем рекламу по умолчанию
|
|||
|
|
loadVastAd('https://platform-staging.reelcontent.com/api/public/vast/2.0/tag?campaign=cam-e951792a909f17');
|
|||
|
|
|
|||
|
|
}(window.VASTPlayer));
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|