215 lines
6.8 KiB
PHP
215 lines
6.8 KiB
PHP
<?php
|
|
// Fehlerberichterstattung für Debugging
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 0); // Keine Ausgabe, nur JSON
|
|
|
|
header('Content-Type: application/json');
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: POST');
|
|
header('Access-Control-Allow-Headers: Content-Type');
|
|
|
|
// Fehlerbehandlung
|
|
set_error_handler(function($errno, $errstr, $errfile, $errline) {
|
|
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
|
});
|
|
|
|
try {
|
|
// Nur POST-Anfragen erlauben
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
http_response_code(405);
|
|
echo json_encode(['error' => 'Nur POST-Anfragen erlaubt']);
|
|
exit;
|
|
}
|
|
|
|
// Lese JSON-Input
|
|
$rawInput = file_get_contents('php://input');
|
|
$input = json_decode($rawInput, true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
throw new Exception('Ungültiges JSON: ' . json_last_error_msg());
|
|
}
|
|
|
|
if (!isset($input['playlistId']) || !isset($input['apiKey'])) {
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'playlistId und apiKey sind erforderlich']);
|
|
exit;
|
|
}
|
|
|
|
$playlistId = $input['playlistId'];
|
|
$apiKey = $input['apiKey'];
|
|
|
|
// Validierung
|
|
if (empty($playlistId) || empty($apiKey)) {
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'playlistId und apiKey dürfen nicht leer sein']);
|
|
exit;
|
|
}
|
|
|
|
$videos = [];
|
|
$pageToken = '';
|
|
$maxResults = 50;
|
|
$playlistTitle = '';
|
|
|
|
// Prüfe ob cURL verfügbar ist
|
|
if (!function_exists('curl_init')) {
|
|
throw new Exception('cURL ist nicht verfügbar. Bitte aktiviere die cURL-Extension in PHP.');
|
|
}
|
|
|
|
// Hole Playlist-Informationen (Titel)
|
|
$playlistInfoUrl = "https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=" . urlencode($playlistId) . "&key=" . urlencode($apiKey);
|
|
$playlistInfoResponse = curlRequest($playlistInfoUrl);
|
|
|
|
if ($playlistInfoResponse !== false) {
|
|
$playlistInfoData = json_decode($playlistInfoResponse, true);
|
|
if (isset($playlistInfoData['items'][0]['snippet']['title'])) {
|
|
$playlistTitle = $playlistInfoData['items'][0]['snippet']['title'];
|
|
}
|
|
}
|
|
|
|
// Hole alle Videos der Playlist (paginiert)
|
|
do {
|
|
$url = "https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails&playlistId=" . urlencode($playlistId) . "&maxResults=" . $maxResults . "&pageToken=" . urlencode($pageToken) . "&key=" . urlencode($apiKey);
|
|
|
|
$response = curlRequest($url);
|
|
|
|
if ($response === false) {
|
|
throw new Exception('API-Anfrage fehlgeschlagen (Netzwerkfehler)');
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
throw new Exception('Ungültige JSON-Antwort von YouTube API');
|
|
}
|
|
|
|
if (isset($data['error'])) {
|
|
$errorMsg = $data['error']['message'] ?? 'YouTube API Fehler';
|
|
$errorCode = $data['error']['code'] ?? 'unbekannt';
|
|
throw new Exception("YouTube API Fehler ($errorCode): $errorMsg");
|
|
}
|
|
|
|
if (!isset($data['items'])) {
|
|
throw new Exception('Ungültige API-Antwort: items fehlt');
|
|
}
|
|
|
|
// Sammle Video-IDs
|
|
$videoIds = array_map(function($item) {
|
|
return $item['contentDetails']['videoId'];
|
|
}, $data['items']);
|
|
|
|
if (empty($videoIds)) {
|
|
break; // Keine Videos mehr
|
|
}
|
|
|
|
// Hole Video-Details (inkl. Upload-Datum und Dauer)
|
|
$videoDetailsUrl = "https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=" . implode(',', $videoIds) . "&key=" . urlencode($apiKey);
|
|
$videoResponse = curlRequest($videoDetailsUrl);
|
|
|
|
if ($videoResponse === false) {
|
|
throw new Exception('Video-Details konnten nicht geladen werden');
|
|
}
|
|
|
|
$videoData = json_decode($videoResponse, true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
throw new Exception('Ungültige JSON-Antwort bei Video-Details');
|
|
}
|
|
|
|
if (isset($videoData['error'])) {
|
|
$errorMsg = $videoData['error']['message'] ?? 'YouTube API Fehler';
|
|
throw new Exception("YouTube API Fehler bei Video-Details: $errorMsg");
|
|
}
|
|
|
|
// Extrahiere Upload-Daten und Dauer
|
|
if (isset($videoData['items'])) {
|
|
foreach ($videoData['items'] as $video) {
|
|
$videos[] = [
|
|
'publishedAt' => $video['snippet']['publishedAt'] ?? null,
|
|
'duration' => $video['contentDetails']['duration'] ?? null
|
|
];
|
|
}
|
|
}
|
|
|
|
$pageToken = $data['nextPageToken'] ?? '';
|
|
} while ($pageToken);
|
|
|
|
// Berechne Gesamtdauer
|
|
$totalMinutes = 0;
|
|
foreach ($videos as $video) {
|
|
if ($video['duration']) {
|
|
$totalMinutes += parseDuration($video['duration']);
|
|
}
|
|
}
|
|
|
|
// Finde ältestes und neuestes Datum
|
|
$dates = array_filter(array_map(function($video) {
|
|
return $video['publishedAt'];
|
|
}, $videos));
|
|
|
|
sort($dates);
|
|
|
|
$oldestDate = count($dates) > 0 ? substr($dates[0], 0, 10) : null;
|
|
$newestDate = count($dates) > 0 ? substr($dates[count($dates) - 1], 0, 10) : null;
|
|
|
|
// Sende Ergebnis
|
|
echo json_encode([
|
|
'playlistTitle' => $playlistTitle,
|
|
'videoCount' => count($videos),
|
|
'totalMinutes' => $totalMinutes,
|
|
'oldestDate' => $oldestDate,
|
|
'newestDate' => $newestDate
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
http_response_code(500);
|
|
echo json_encode([
|
|
'error' => $e->getMessage(),
|
|
'file' => basename($e->getFile()),
|
|
'line' => $e->getLine()
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Macht HTTP-Anfrage mit cURL
|
|
*/
|
|
function curlRequest($url) {
|
|
$ch = curl_init();
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, 'YouTube Playlist Analyzer/1.0');
|
|
|
|
$response = curl_exec($ch);
|
|
$error = curl_error($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
curl_close($ch);
|
|
|
|
if ($response === false) {
|
|
throw new Exception('cURL-Fehler: ' . $error);
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Konvertiert ISO 8601 Duration in Minuten
|
|
* Format: PT#H#M#S
|
|
*/
|
|
function parseDuration($duration) {
|
|
preg_match('/PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/', $duration, $matches);
|
|
|
|
$hours = isset($matches[1]) ? (int)$matches[1] : 0;
|
|
$minutes = isset($matches[2]) ? (int)$matches[2] : 0;
|
|
$seconds = isset($matches[3]) ? (int)$matches[3] : 0;
|
|
|
|
// Auf volle Minuten aufrunden
|
|
$totalMinutes = $hours * 60 + $minutes + ceil($seconds / 60);
|
|
|
|
return $totalMinutes;
|
|
}
|
|
?>
|