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;
 | |
| }
 | |
| ?>
 | 
