Neu: YouTube Playlist Analyzer
This commit is contained in:
214
yt_playlist/fetch_playlist.php
Normal file
214
yt_playlist/fetch_playlist.php
Normal file
@@ -0,0 +1,214 @@
|
||||
<?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;
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user