[ 'timeout' => 8, 'user_agent' => 'Mozilla/5.0 (compatible; podcast-proxy/1.0)', ]]); $xml = @file_get_contents($rss_url, false, $ctx); if ($xml === false) { http_response_code(502); echo json_encode(['error' => 'could not fetch feed']); exit; } $feed = @simplexml_load_string($xml); if (!$feed) { http_response_code(502); echo json_encode(['error' => 'could not parse feed']); exit; } $ns_itunes = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; // artwork from channel-level itunes:image $channel = $feed->channel; $itunes_ch = $channel->children($ns_itunes); $art = (string)($itunes_ch->image->attributes()['href'] ?? ''); $episodes = []; foreach ($channel->item as $item) { if (count($episodes) >= $limit) break; $itunes = $item->children($ns_itunes); $duration_raw = (string)($itunes->duration ?? ''); // normalize duration to h:mm or Xm if (strpos($duration_raw, ':') !== false) { $parts = array_map('intval', explode(':', $duration_raw)); if (count($parts) === 3) { [$h, $m, $s] = $parts; $duration = $h > 0 ? "{$h}:" . str_pad($m, 2, '0', STR_PAD_LEFT) : "{$m}m"; } else { [$m, $s] = $parts; $duration = "{$m}m"; } } elseif (is_numeric($duration_raw)) { $secs = intval($duration_raw); $h = intdiv($secs, 3600); $m = intdiv($secs % 3600, 60); $duration = $h > 0 ? "{$h}:" . str_pad($m, 2, '0', STR_PAD_LEFT) : "{$m}m"; } else { $duration = ''; } $link = (string)($item->link ?? $item->enclosure->attributes()['url'] ?? '#'); $episodes[] = [ 'title' => (string)$item->title, 'url' => $link, 'duration' => $duration, ]; } echo json_encode(['art' => $art, 'episodes' => $episodes]);