high declare(strict_types=1); ini_set('display_errors', '1'); error_reporting(E_ALL); const API_BASE = 'https://www.googleapis.com/youtube/v3'; const API_KEY_FALLBACK = 'Hier moet je je API key van YT invullen'; // Mijn Youtube API Key function api_key(): string { $k = getenv('YT_API_KEY'); if (!$k || $k === '') $k = API_KEY_FALLBACK; if (!$k || $k === '') { http_response_code(500); exit('Fout: zet je API key in env YT_API_KEY of in API_KEY_FALLBACK'); } return $k; } function http_get(string $path, array $params): array { $url = API_BASE.$path.'?'.http_build_query($params); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_HTTPHEADER => ['Accept: application/json'] ]); $res = curl_exec($ch); if ($res === false) { $err = curl_error($ch); curl_close($ch); exit('cURL error: '.$err); } $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code < 200 || $code >= 300) exit('HTTP error '.$code.': '.$res); $data = json_decode($res, true); if (!is_array($data)) exit('JSON parse error'); return $data; } function iso_utc(DateTime $dt): string { return $dt->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d\TH:i:s\Z'); } function days_since(string $iso): int { $dt = DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $iso, new DateTimeZone('UTC')); if (!$dt) return 999999; $now = new DateTime('now', new DateTimeZone('UTC')); $days = (int) floor(($now->getTimestamp() - $dt->getTimestamp()) / 86400); return max(1, $days); } function fetch_video_stats(string $key, array $ids): array { if (!$ids) return []; $params = [ 'key' => $key, 'part' => 'snippet,statistics', 'id' => implode(',', $ids) ]; $data = http_get('/videos', $params); $out = []; foreach (($data['items'] ?? []) as $it) { $sn = $it['snippet'] ?? []; $st = $it['statistics'] ?? []; $views = isset($st['viewCount']) ? (int)$st['viewCount'] : 0; $out[] = [ 'id' => (string)($it['id'] ?? ''), 'title' => (string)($sn['title'] ?? ''), 'channel' => (string)($sn['channelTitle'] ?? ''), 'views' => $views, 'publishedAt' => (string)($sn['publishedAt'] ?? '1970-01-01T00:00:00Z') ]; } return $out; } function search_videos(string $key, string $query, int $pages, ?string $region, ?string $published_after, string $safe): array { $videos = []; $page_token = null; for ($i = 0; $i < $pages; $i++) { $params = [ 'key' => $key, 'part' => 'snippet', 'type' => 'video', 'q' => $query, 'maxResults' => 50, 'safeSearch' => $safe ]; if ($region) $params['regionCode'] = $region; if ($published_after) $params['publishedAfter'] = $published_after; if ($page_token) $params['pageToken'] = $page_token; $data = http_get('/search', $params); $ids = []; foreach (($data['items'] ?? []) as $it) { $id = $it['id']['videoId'] ?? null; if ($id) $ids[] = $id; } if ($ids) { $stats = fetch_video_stats($key, $ids); $videos = array_merge($videos, $stats); } $page_token = $data['nextPageToken'] ?? null; if (!$page_token) break; usleep(100000); } return $videos; } // ---------- UI + logica ---------- $q = trim($_GET['q'] ?? ''); $pages = max(1, (int)($_GET['pages'] ?? 2)); $max_views = isset($_GET['max_views']) && $_GET['max_views'] !== '' ? max(0, (int)$_GET['max_views']) : null; $days = isset($_GET['days']) && $_GET['days'] !== '' ? max(1, (int)$_GET['days']) : null; $region = $_GET['region'] ?? ''; $sort = in_array(($_GET['sort'] ?? 'views'), ['views','ratio'], true) ? $_GET['sort'] : 'views'; $limit = max(1, min(200, (int)($_GET['limit'] ?? 50))); $safe = in_array(($_GET['safe'] ?? 'none'), ['none','moderate','strict'], true) ? $_GET['safe'] : 'none'; $videos = []; if ($q !== '') { $key = api_key(); $published_after = null; if ($days) { $since = new DateTime('now', new DateTimeZone('UTC')); $since->modify('-'.$days.' days'); $published_after = iso_utc($since); } $videos = search_videos($key, $q, $pages, $region ?: null, $published_after, $safe); // filters $filtered = []; foreach ($videos as $v) { if ($max_views !== null && $v['views'] > $max_views) continue; if ($days !== null && days_since($v['publishedAt']) > $days) continue; $filtered[] = $v; } // sort if ($sort === 'views') { usort($filtered, function($a, $b) { return $a['views'] <=> $b['views']; }); } else { usort($filtered, function($a, $b) { $ra = $a['views'] / days_since($a['publishedAt']); $rb = $b['views'] / days_since($b['publishedAt']); if ($ra == $rb) return 0; return ($ra < $rb) ? -1 : 1; }); } $videos = array_slice($filtered, 0, $limit); } function h(string $s): string { return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } ?>
| # | Video | Views | Leeftijd | Channel |
|---|---|---|---|---|
| =($i+1)?> |
=h($v['title'])?> |
=number_format($v['views'], 0, ',', '.')?> | =$age?> d | =h($v['channel'])?> |