<?php // watch.php β Channel detail + player (StreamHub) declare(strict_types=1); require_once __DIR__ . '/includes/db.php'; require_once __DIR__ . '/includes/helpers.php'; require_once __DIR__ . '/includes/settings.php'; $pdo = sh_db(); // Helper for watch URL if (!function_exists('sh_channel_watch_url')) { function sh_channel_watch_url(array $ch): string { $slug = trim((string)($ch['slug'] ?? '')); if ($slug !== '') { return '/watch/' . rawurlencode($slug); } return '/watch/' . (int)($ch['id'] ?? 0); } } /* ------------------------------------------------- 1) Load channel by slug or ID ------------------------------------------------- */ $slugParam = trim($_GET['slug'] ?? ''); $idParam = isset($_GET['id']) ? (int)$_GET['id'] : 0; $channel = null; if ($slugParam !== '') { $st = $pdo->prepare(' SELECT c.*, cat.name AS category_name FROM tv_channels c LEFT JOIN tv_categories cat ON cat.id = c.category_id WHERE c.slug = ? AND c.is_active = 1 LIMIT 1 '); $st->execute([$slugParam]); $channel = $st->fetch(PDO::FETCH_ASSOC); } elseif ($idParam > 0) { $st = $pdo->prepare(' SELECT c.*, cat.name AS category_name FROM tv_channels c LEFT JOIN tv_categories cat ON cat.id = c.category_id WHERE c.id = ? AND c.is_active = 1 LIMIT 1 '); $st->execute([$idParam]); $channel = $st->fetch(PDO::FETCH_ASSOC); } if (!$channel) { $siteName = get_setting('site_name', 'StreamHub'); $pageTitle = 'Channel not found'; require __DIR__ . '/templates/header.php'; ?> <section class="sh-section"> <div class="container"> <div class="notification is-danger is-light"> Channel not found or is currently inactive. </div> <p> <a href="/browse.php" class="button is-link is-light"> <span class="icon"><i class="fa-solid fa-arrow-left"></i></span> <span>Back to browse</span> </a> </p> </div> </section> <?php require __DIR__ . '/templates/footer.php'; exit; } $siteName = get_setting('site_name', 'StreamHub'); $pageTitle = $channel['name'] . ' β Watch Live'; // Original DB URL $streamUrl = (string)$channel['stream_url']; $catId = isset($channel['category_id']) ? (int)$channel['category_id'] : 0; $channelId = (int)$channel['id']; /* ------------------------------------------------- Decide whether to use HLS proxy - Proxy ALL .m3u8 (http or https) so keys & segments also go through our proxy (fixes CORS, mixed-content). - Non-.m3u8 URLs are played directly. ------------------------------------------------- */ $parsed = @parse_url($streamUrl) ?: []; $path = (string)($parsed['path'] ?? ''); $isM3u8 = (bool)preg_match('~\.m3u8($|[?#])~i', $path); $useProxy = $isM3u8; $playerUrl = $useProxy ? '/hls-proxy.php?c=' . $channelId : $streamUrl; /* ------------------------------------------------- 2) Related channels (same category, exclude current) ------------------------------------------------- */ $relatedChannels = []; if ($catId > 0) { $relSt = $pdo->prepare(' SELECT c.id, c.name, c.slug, c.logo, c.description, c.country_code, c.is_featured, cat.name AS category_name FROM tv_channels c LEFT JOIN tv_categories cat ON cat.id = c.category_id WHERE c.is_active = 1 AND c.category_id = ? AND c.id <> ? ORDER BY c.is_featured DESC, c.created_at DESC LIMIT 12 '); $relSt->execute([$catId, $channelId]); $relatedChannels = $relSt->fetchAll(PDO::FETCH_ASSOC) ?: []; } if (!$relatedChannels) { $relSt = $pdo->prepare(' SELECT c.id, c.name, c.slug, c.logo, c.description, c.country_code, c.is_featured, cat.name AS category_name FROM tv_channels c LEFT JOIN tv_categories cat ON cat.id = c.category_id WHERE c.is_active = 1 AND c.id <> ? ORDER BY c.created_at DESC LIMIT 12 '); $relSt->execute([$channelId]); $relatedChannels = $relSt->fetchAll(PDO::FETCH_ASSOC) ?: []; } /* ------------------------------------------------- 3) Sidebar: some more channels ------------------------------------------------- */ $sidebarSt = $pdo->query(' SELECT c.id, c.name, c.slug, c.logo, c.country_code, c.is_featured FROM tv_channels c WHERE c.is_active = 1 ORDER BY c.is_featured DESC, c.created_at DESC LIMIT 10 '); $sidebarChannels = $sidebarSt->fetchAll(PDO::FETCH_ASSOC) ?: []; require __DIR__ . '/templates/header.php'; ?> <section class="sh-section"> <div class="container"> <div class="columns is-variable is-5"> <!-- Main player + info --> <div class="column is-9-desktop is-12-tablet"> <div class="box sh-watch-main"> <!-- Header row --> <div class="sh-watch-header mb-3"> <div class="media"> <div class="media-left"> <figure class="image is-64x64 sh-watch-logo"> <?php if (!empty($channel['logo'])): ?> <img src="<?= sh_h($channel['logo']) ?>" alt="<?= sh_h($channel['name']) ?>"> <?php else: ?> <span class="icon is-large"> <i class="fa-solid fa-tv"></i> </span> <?php endif; ?> </figure> </div> <div class="media-content"> <h1 class="title is-4 mb-1"> <?= sh_h($channel['name']) ?> <?php if (!empty($channel['is_featured'])): ?> <span class="tag is-warning is-light is-size-7 ml-2"> Featured </span> <?php endif; ?> </h1> <p class="is-size-7 has-text-grey"> <?php if (!empty($channel['category_name'])): ?> <span class="tag is-light mr-1"> <span class="icon is-small"><i class="fa-solid fa-layer-group"></i></span> <span><?= sh_h($channel['category_name']) ?></span> </span> <?php endif; ?> <?php if (!empty($channel['country_code'])): ?> <span class="tag is-light mr-1"> <span class="icon is-small"><i class="fa-solid fa-earth-europe"></i></span> <span><?= sh_h($channel['country_code']) ?></span> </span> <?php endif; ?> <span class="tag is-success is-light"> <span class="icon is-small"><i class="fa-solid fa-circle-play"></i></span> <span>Live</span> </span> </p> </div> </div> </div> <!-- Player --> <div class="sh-player-wrapper mb-4"> <div class="sh-player-inner"> <video id="shPlayer" class="sh-player-video" controls playsinline <?php if (!empty($channel['logo'])): ?> poster="<?= sh_h($channel['logo']) ?>" <?php endif; ?> > <!-- No direct src here, we set it via JS --> Your browser does not support the video tag. </video> </div> <p class="is-size-7 has-text-grey mt-1"> If the stream doesnβt start, try reloading or using a modern browser with HLS support. </p> </div> <!-- Channel details --> <div class="sh-watch-details"> <h2 class="title is-5 mb-2">Channel details</h2> <div class="columns is-multiline is-variable is-3"> <div class="column is-6-tablet is-4-desktop"> <p class="has-text-grey is-size-7 mb-1">Name</p> <p class="is-size-6"><?= sh_h($channel['name']) ?></p> </div> <?php if (!empty($channel['category_name'])): ?> <div class="column is-6-tablet is-4-desktop"> <p class="has-text-grey is-size-7 mb-1">Category</p> <p class="is-size-6"> <?= sh_h($channel['category_name']) ?> </p> </div> <?php endif; ?> <?php if (!empty($channel['country_code'])): ?> <div class="column is-6-tablet is-4-desktop"> <p class="has-text-grey is-size-7 mb-1">Country</p> <p class="is-size-6"> <?= sh_h($channel['country_code']) ?> </p> </div> <?php endif; ?> <div class="column is-6-tablet is-4-desktop"> <p class="has-text-grey is-size-7 mb-1">Status</p> <p class="is-size-6"> <?php if (!empty($channel['is_active'])): ?> <span class="tag is-success is-light is-size-7">Active</span> <?php else: ?> <span class="tag is-danger is-light is-size-7">Inactive</span> <?php endif; ?> </p> </div> </div> <?php if (!empty($channel['description'])): ?> <div class="mt-3"> <p class="has-text-grey is-size-7 mb-1">Description</p> <p class="is-size-6"> <?= nl2br(sh_h($channel['description'])) ?> </p> </div> <?php endif; ?> </div> </div><!-- /.box --> </div><!-- /.column main --> <!-- Sidebar --> <div class="column is-3-desktop is-12-tablet"> <div class="box sh-watch-sidebar"> <h3 class="title is-6 mb-3"> More channels </h3> <?php if (!$sidebarChannels): ?> <p class="is-size-7 has-text-grey">No other channels found.</p> <?php else: ?> <div class="sh-sidebar-list"> <?php foreach ($sidebarChannels as $side): ?> <a href="<?= sh_h(sh_channel_watch_url($side)) ?>" class="sh-sidebar-item media"> <div class="media-left"> <figure class="image is-32x32"> <?php if (!empty($side['logo'])): ?> <img src="<?= sh_h($side['logo']) ?>" alt="<?= sh_h($side['name']) ?>"> <?php else: ?> <span class="icon is-small"> <i class="fa-solid fa-tv"></i> </span> <?php endif; ?> </figure> </div> <div class="media-content"> <p class="is-size-7 has-text-weight-semibold mb-0"> <?= sh_h($side['name']) ?> </p> <p class="is-size-7 has-text-grey"> <?php if (!empty($side['country_code'])): ?> <?= sh_h($side['country_code']) ?> <?php else: ?> <?php endif; ?> </p> </div> <?php if (!empty($side['is_featured'])): ?> <div class="media-right"> <span class="icon has-text-warning is-size-7"> <i class="fa-solid fa-star"></i> </span> </div> <?php endif; ?> </a> <?php endforeach; ?> </div> <?php endif; ?> </div> </div><!-- /.column sidebar --> </div><!-- /.columns --> </div> </section> <!-- Related Channels --> <section class="sh-section"> <div class="container"> <div class="sh-section-header"> <div> <h2 class="sh-section-title"> Related channels </h2> <p class="sh-section-subtitle"> More channels you might like. </p> </div> <a class="sh-section-link" href="/browse.php"> Browse all <span class="icon is-small"><i class="fa-solid fa-arrow-right-long"></i></span> </a> </div> <div class="columns is-multiline is-variable is-4"> <?php if (!$relatedChannels): ?> <div class="column is-12"> <p class="has-text-grey is-size-7"> No related channels found. </p> </div> <?php else: ?> <?php foreach ($relatedChannels as $ch): ?> <div class="column is-3-desktop is-4-tablet is-6-mobile"> <a href="<?= sh_h(sh_channel_watch_url($ch)) ?>" class="sh-channel-poster"> <div class="sh-channel-thumb"> <?php if (!empty($ch['logo'])): ?> <img src="<?= sh_h($ch['logo']) ?>" alt="<?= sh_h($ch['name']) ?>"> <?php else: ?> <span class="icon is-large"> <i class="fa-solid fa-tv"></i> </span> <?php endif; ?> </div> <div class="sh-channel-meta"> <div class="sh-channel-title"> <?= sh_h($ch['name']) ?> <?php if (!empty($ch['is_featured'])): ?> <span class="tag is-warning is-light is-size-7 ml-1">Featured</span> <?php endif; ?> </div> <?php if (!empty($ch['description'])): ?> <div class="sh-channel-desc"> <?= sh_h(mb_strimwidth((string)$ch['description'], 0, 80, 'β¦', 'UTF-8')) ?> </div> <?php endif; ?> <div class="sh-channel-meta-line"> <?php if (!empty($ch['category_name'])): ?> <span class="sh-channel-chip"> <?= sh_h($ch['category_name']) ?> </span> <?php endif; ?> <?php if (!empty($ch['country_code'])): ?> <span class="sh-channel-country"> <span class="sh-flag"><?= sh_h($ch['country_code']) ?></span> </span> <?php endif; ?> </div> </div> </a> </div> <?php endforeach; ?> <?php endif; ?> </div> </div> </section> <!-- HLS.js for .m3u8 --> <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script> <script> (function() { const video = document.getElementById('shPlayer'); if (!video) return; const streamUrl = <?= json_encode($playerUrl, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>; if (!streamUrl) return; // Decide if we should treat this as HLS const isHls = streamUrl.indexOf('hls-proxy.php') !== -1 || /\.m3u8($|\?)/i.test(streamUrl); if (window.Hls && Hls.isSupported() && isHls) { const hls = new Hls(); hls.loadSource(streamUrl); hls.attachMedia(video); } else { // Direct URL (works for tvpass-style links, MP4, etc.) video.src = streamUrl; } })(); </script> <?php require __DIR__ . '/templates/footer.php';