<?php
/**
* Lịch trực tiếp bóng đá - WordPress Shortcode
* Sử dụng: [live_soccer]
* Hoặc: [live_soccer bg="URL_HINH_NEN"]
*/
add_shortcode('live_soccer', function($atts) {
$atts = shortcode_atts([
'bg' => 'https://cdn-media.sforum.vn/storage/app/media/wp-content/uploads/2023/06/hinh-nen-bong-da-thumb.jpg',
], $atts);
ob_start();
// Constants
define('SOCCER_BASE', 'https://livesoccerapi.com');
define('SOCCER_SRC_PATH', '/truc-tiep-bong-da/');
define('SOCCER_GOLD1', '#f6e3b0');
define('SOCCER_GOLD2', '#8f6a2b');
// Helper functions
function soccer_http_get($url) {
$response = wp_remote_get($url, [
'timeout' => 25,
'sslverify' => false,
'headers' => [
'User-Agent' => 'Mozilla/5.0'
]
]);
if (is_wp_error($response)) {
return '';
}
return wp_remote_retrieve_body($response);
}
function soccer_xpath_new($html) {
libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $html);
libxml_clear_errors();
return new DOMXPath($dom);
}
function soccer_xp_first_text($xp, $ctx, $q) {
$n = $xp->query($q, $ctx);
if ($n && $n->length) {
return trim(preg_replace('/\s+/u', ' ', $n->item(0)->textContent));
}
return null;
}
function soccer_xp_first_attr($xp, $ctx, $q, $attr) {
$n = $xp->query($q, $ctx);
if ($n && $n->length) {
$el = $n->item(0);
$v = $el->getAttribute($attr);
return $v !== '' ? $v : null;
}
return null;
}
function soccer_clean_team_name($s) {
$s = (string)$s;
$s = preg_replace('/[&#].*$/u', '', $s);
$s = preg_replace('/\s+/u', ' ', $s);
$s = trim($s, " \t\n\r\0\x0B-–—·•|");
return trim($s);
}
function soccer_build_url($page, $bg) {
$current_url = add_query_arg(null, null);
return add_query_arg(['soccer_page' => $page], remove_query_arg(['soccer_page', 'q', 'live'], $current_url));
}
// Get page number
$p = max(1, isset($_GET['soccer_page']) ? intval($_GET['soccer_page']) : 1);
$bg = esc_url($atts['bg']);
// Fetch data
$srcUrl = SOCCER_BASE . SOCCER_SRC_PATH . '?page=' . $p;
$html = soccer_http_get($srcUrl);
$xp = soccer_xpath_new($html);
$cards = $xp->query("//div[contains(@class,'card') and contains(@class,'matches')]");
$matches = [];
foreach ($cards as $card) {
$date = soccer_xp_first_text($xp, $card, ".//div[contains(@class,'date-match')]");
$home_name = soccer_xp_first_text($xp, $card, ".//div[contains(@class,'team-home')]//div[1]");
$home_img = soccer_xp_first_attr($xp, $card, ".//div[contains(@class,'team-home')]//img", "src");
$away_name = soccer_xp_first_text($xp, $card, ".//div[contains(@class,'team-away')]//div[1]");
$away_img = soccer_xp_first_attr($xp, $card, ".//div[contains(@class,'team-away')]//img", "src");
$score = soccer_xp_first_text($xp, $card, ".//div[contains(@class,'score')]");
$league_bottom = soccer_xp_first_text($xp, $card, ".//div[contains(@class,'status') and not(contains(@class,'green'))]");
$league_top = soccer_xp_first_text($xp, $card, ".//div[contains(@class,'status') and contains(@class,'green')]");
$home_name = soccer_clean_team_name($home_name);
$away_name = soccer_clean_team_name($away_name);
$detail = soccer_xp_first_attr($xp, $card, ".//a[contains(@class,'toDetail')]", "href");
if ($detail && strpos($detail, 'http') !== 0) {
$detail = SOCCER_BASE . $detail;
}
$is_live = false;
$liveNode = $xp->query(".//div[contains(@class,'bet')]//h5[contains(@class,'live-match')]", $card);
if ($liveNode && $liveNode->length && strtoupper(trim($liveNode->item(0)->textContent)) === 'LIVE') {
$is_live = true;
}
$matches[] = [
'date' => $date ?: 'N/A',
'home' => $home_name,
'home_img' => $home_img,
'away' => $away_name,
'away_img' => $away_img,
'time_or_score' => $score ?: '',
'league_top' => $league_top ?: '',
'league' => $league_bottom ?: '',
'detail' => $detail,
'is_live' => $is_live,
];
}
// Pagination
$paginationLinks = [];
if ($aNodes = $xp->query("//a[contains(@href,'?page=')]")) {
foreach ($aNodes as $a) {
$href = $a->getAttribute('href');
if (preg_match('/[?&]page=(\d+)/', $href, $m)) {
$paginationLinks[(int)$m[1]] = true;
}
}
}
$totalPages = max(1, !empty($paginationLinks) ? max(array_keys($paginationLinks)) : 1);
// Group by date
$byDate = [];
foreach ($matches as $m) {
$byDate[$m['date']][] = $m;
}
?>
<style>
.soccer-wrap {
font-family: Inter, system-ui, Arial;
background: #0b0f1a url('<?php echo $bg; ?>') center/cover fixed no-repeat;
color: #1a2537;
max-width: 1280px;
margin: 0 auto;
padding: 20px;
border-radius: 12px;
}
.soccer-wrap * { box-sizing: border-box; }
.soccer-page-title {
color: #fff;
font-weight: 900;
font-size: 26px;
text-align: center;
margin-bottom: 20px;
}
#soccer-video-section {
display: none;
max-width: 1200px;
margin: 20px auto;
background: linear-gradient(180deg, #081228, #0a1a34);
border-radius: 14px;
border: 1px solid #1a2f55;
overflow: hidden;
position: relative;
}
#soccer-video-section.active { display: block; }
.soccer-close-video {
position: absolute;
top: 12px;
right: 12px;
z-index: 1000;
background: rgba(231, 76, 60, 0.95);
color: #fff;
border: none;
width: 42px;
height: 42px;
border-radius: 50%;
cursor: pointer;
font-weight: 700;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
transition: all 0.3s ease;
}
.soccer-close-video:hover {
background: rgba(192, 57, 43, 1);
transform: scale(1.1);
}
#soccer-player-container {
width: 100%;
aspect-ratio: 16/9;
background: #000;
position: relative;
}
#soccer-player-container iframe {
width: 100%;
height: 100%;
border: none;
}
.soccer-ad-banner {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 80px;
background: #ff0000;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 900;
font-size: 20px;
}
.soccer-loading {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #fff;
font-size: 18px;
}
.soccer-date-pill {
background: rgba(255, 255, 255, .15);
padding: 6px 12px;
border-radius: 12px;
color: #fff;
font-weight: 800;
margin: 12px auto;
display: block;
width: max-content;
}
.soccer-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
gap: 16px;
justify-content: center;
padding: 0 12px;
margin-bottom: 20px;
}
.soccer-match-link {
display: block;
text-decoration: none;
color: inherit;
cursor: pointer;
}
.soccer-match-card {
position: relative;
border-radius: 12px;
overflow: hidden;
background: linear-gradient(180deg, #ffffff, #eef2f8);
box-shadow: 0 8px 20px rgba(0, 0, 0, .35);
margin: 0 auto;
transition: transform .2s;
max-width: 95%;
}
.soccer-match-card:hover { transform: translateY(-2px); }
.soccer-match-card::before {
content: "";
position: absolute;
inset: -1px;
border-radius: 12px;
padding: .5px;
background: linear-gradient(135deg, <?php echo SOCCER_GOLD1; ?>, <?php echo SOCCER_GOLD2; ?>);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.soccer-card-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 10px 12px 0;
}
.soccer-time-tag {
font-size: 13px;
font-weight: 900;
background: #fff;
border: 1px solid #e4e7ef;
padding: 4px 8px;
border-radius: 6px;
}
.soccer-league-tag {
font-size: 12px;
font-weight: 900;
background: #e8f7f0;
color: #0f5132;
border: 1px solid #b6e1cf;
padding: 4px 8px;
border-radius: 6px;
white-space: nowrap;
max-width: 60%;
overflow: hidden;
text-overflow: ellipsis;
}
.soccer-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 10px 12px;
}
.soccer-team {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
}
.soccer-team.right {
flex-direction: row-reverse;
text-align: right;
}
.soccer-logo {
width: 56px;
height: 56px;
display: grid;
place-items: center;
background: #fff;
border-radius: 8px;
border: 1px solid #e2e6f1;
overflow: hidden;
}
.soccer-logo img {
max-width: 100%;
max-height: 100%;
}
.soccer-name {
font-size: 13px;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 150px;
color: #1b2435;
}
.soccer-vs {
display: grid;
place-items: center;
min-width: 36px;
height: 36px;
padding: 0 8px;
border-radius: 8px;
background: #fff;
border: 1px solid #e4e7ef;
font-weight: 900;
color: #2a3a53;
font-size: 12px;
}
.soccer-card-bottom {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 0 12px 12px;
}
.soccer-btn {
padding: 7px 10px;
border-radius: 8px;
font-weight: 800;
font-size: 12.5px;
text-align: center;
min-width: 110px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.soccer-btn-muted {
background: #eef2f8;
color: #364760;
}
.soccer-btn-primary {
background: #ffc94a;
color: #1b1203;
}
.soccer-btn-live {
background: #e74c3c;
color: #fff;
}
.soccer-pagination {
display: flex;
gap: 6px;
flex-wrap: wrap;
margin: 24px auto;
justify-content: center;
}
.soccer-pagination a,
.soccer-pagination span {
padding: 8px 12px;
border: 1px solid rgba(255, 255, 255, .4);
border-radius: 10px;
background: rgba(255, 255, 255, .15);
color: #fff;
text-decoration: none;
font-weight: 700;
}
.soccer-pagination .active {
background: #3b82f6;
border-color: #3b82f6;
color: #fff;
}
.soccer-pagination .disabled {
opacity: .45;
pointer-events: none;
}
@media(max-width:640px) {
.soccer-grid {
grid-template-columns: 1fr;
gap: 14px;
}
.soccer-match-card { max-width: 92%; }
.soccer-page-title { font-size: 20px; }
.soccer-name {
max-width: 65vw;
font-size: 12px;
}
.soccer-logo {
width: 44px;
height: 44px;
}
.soccer-vs {
min-width: 30px;
height: 30px;
font-size: 11px;
}
#soccer-player-container {
height: 50vh;
aspect-ratio: unset;
}
.soccer-ad-banner {
height: 70px;
font-size: 16px;
}
.soccer-close-video {
width: 36px;
height: 36px;
font-size: 18px;
}
}
</style>
<div class="soccer-wrap">
<div class="soccer-page-title">Lịch trực tiếp bóng đá</div>
<div id="soccer-video-section">
<button class="soccer-close-video" onclick="soccerCloseVideo()">✕</button>
<div id="soccer-player-container">
<div class="soccer-ad-banner">🚫 BANNER CHE QUẢNG CÁO</div>
<div class="soccer-loading">Đang tải video...</div>
</div>
</div>
<?php if (empty($byDate)): ?>
<div class="soccer-date-pill">Không có trận phù hợp ở trang <?php echo (int)$p; ?>.</div>
<?php else: ?>
<?php foreach ($byDate as $date => $items): ?>
<div class="soccer-date-pill"><?php echo esc_html($date); ?></div>
<div class="soccer-grid">
<?php foreach ($items as $idx => $m): ?>
<div class="soccer-match-link" onclick="soccerPlayMatch(<?php echo $idx; ?>)">
<div class="soccer-match-card">
<div class="soccer-card-top">
<div class="soccer-time-tag"><?php echo esc_html($m['time_or_score'] ?: '—'); ?></div>
<?php if (!empty($m['league_top'])): ?>
<div class="soccer-league-tag" title="<?php echo esc_attr($m['league_top']); ?>">
<?php echo esc_html($m['league_top']); ?>
</div>
<?php endif; ?>
</div>
<div class="soccer-row">
<div class="soccer-team">
<div class="soccer-logo">
<?php if (!empty($m['home_img'])): ?>
<img src="<?php echo esc_url($m['home_img']); ?>" alt="<?php echo esc_attr($m['home']); ?>">
<?php endif; ?>
</div>
<div class="soccer-name"><?php echo esc_html($m['home']); ?></div>
</div>
<div class="soccer-vs">VS</div>
<div class="soccer-team right">
<div class="soccer-logo">
<?php if (!empty($m['away_img'])): ?>
<img src="<?php echo esc_url($m['away_img']); ?>" alt="<?php echo esc_attr($m['away']); ?>">
<?php endif; ?>
</div>
<div class="soccer-name"><?php echo esc_html($m['away']); ?></div>
</div>
</div>
<div class="soccer-card-bottom">
<?php if ($m['is_live']): ?>
<div class="soccer-btn soccer-btn-live">Đang diễn ra</div>
<?php else: ?>
<div class="soccer-btn soccer-btn-muted">Chưa bắt đầu</div>
<?php endif; ?>
<div class="soccer-btn soccer-btn-primary">Xem trận</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
<div class="soccer-pagination">
<?php
$prev = max(1, $p - 1);
$next = min($totalPages, $p + 1);
$last = max(1, (int)$totalPages);
$window = 2;
$start = max(1, $p - $window);
$end = min($last, $p + $window);
echo '<a class="' . ($p <= 1 ? 'disabled' : '') . '" href="' . esc_url(soccer_build_url($prev, $bg)) . '">« Trước</a>';
if ($start > 1) {
echo '<a href="' . esc_url(soccer_build_url(1, $bg)) . '">1</a>';
if ($start > 2) echo '<span class="disabled">…</span>';
}
for ($i = $start; $i <= $end; $i++) {
if ($i == $p) {
echo '<span class="active">' . $i . '</span>';
} else {
echo '<a href="' . esc_url(soccer_build_url($i, $bg)) . '">' . $i . '</a>';
}
}
if ($end < $last) {
if ($end < $last - 1) echo '<span class="disabled">…</span>';
echo '<a href="' . esc_url(soccer_build_url($last, $bg)) . '">' . $last . '</a>';
}
echo '<a class="' . ($p >= $last ? 'disabled' : '') . '" href="' . esc_url(soccer_build_url($next, $bg)) . '">Sau »</a>';
?>
</div>
</div>
<script>
const soccerMatches = <?php echo json_encode($matches); ?>;
function soccerPlayMatch(idx) {
const match = soccerMatches[idx];
if (!match || !match.detail) {
alert('Không có link video');
return;
}
document.getElementById('soccer-video-section').classList.add('active');
document.getElementById('soccer-video-section').scrollIntoView({
behavior: 'smooth'
});
soccerLoadIframe(match.detail);
}
function soccerLoadIframe(url) {
const container = document.getElementById('soccer-player-container');
const banner = container.querySelector('.soccer-ad-banner');
const iframe = document.createElement('iframe');
iframe.src = url;
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-presentation');
iframe.allowFullscreen = true;
iframe.allow = 'autoplay; fullscreen; picture-in-picture';
iframe.setAttribute('scrolling', 'no');
iframe.setAttribute('frameborder', '0');
container.innerHTML = '';
container.appendChild(iframe);
if (banner) container.appendChild(banner);
}
function soccerCloseVideo() {
document.getElementById('soccer-video-section').classList.remove('active');
const container = document.getElementById('soccer-player-container');
container.innerHTML = '<div class="soccer-ad-banner">🚫 BANNER CHE QUẢNG CÁO</div><div class="soccer-loading">Đang tải video...</div>';
window.scrollTo({ top: 0, behavior: 'smooth' });
}
</script>
<?php
return ob_get_clean();
});