| 282 | |
| 283 | |
| 284 | def fetch_feed_metadata(feed_url: str, timeout: float) -> FeedMetadata: |
| 285 | headers = { |
| 286 | "User-Agent": DEFAULT_USER_AGENT, |
| 287 | "Accept": "application/rss+xml, application/atom+xml, application/xml, text/xml;q=0.9, */*;q=0.8", |
| 288 | } |
| 289 | request = urllib.request.Request(url=feed_url, headers=headers, method="GET") |
| 290 | try: |
| 291 | with urllib.request.urlopen(request, timeout=timeout) as response: |
| 292 | status = getattr(response, "status", response.getcode()) |
| 293 | status_code = int(status) if status is not None else 0 |
| 294 | if status_code >= 400: |
| 295 | raise ValueError(f"Feed request failed with HTTP {status_code}") |
| 296 | return parse_feed_metadata_stream(response, feed_url=feed_url) |
| 297 | except urllib.error.HTTPError as exc: |
| 298 | raise ValueError(f"Feed request failed with HTTP {int(exc.code)}") from exc |
| 299 | except urllib.error.URLError as exc: |
| 300 | raise ValueError(f"Feed request failed: {exc.reason}") from exc |
| 301 | |
| 302 | |
| 303 | def find_existing_category_for_url(body: ET.Element, xml_url: str) -> Optional[str]: |