"""Gitea release provider implementation.""" import json import logging import urllib.error import urllib.request from typing import Optional from .release_provider import ReleaseInfo, ReleaseProvider logger = logging.getLogger(__name__) # Default repository coordinates _DEFAULT_BASE_URL = "https://git.dolgolyov-family.by" _DEFAULT_OWNER = "alexei.dolgolyov" _DEFAULT_REPO = "media-player-server" class GiteaReleaseProvider(ReleaseProvider): """Fetches the latest release from a Gitea repository.""" def __init__( self, base_url: str = _DEFAULT_BASE_URL, owner: str = _DEFAULT_OWNER, repo: str = _DEFAULT_REPO, timeout: float = 10.0, ) -> None: self._api_url = f"{base_url}/api/v1/repos/{owner}/{repo}/releases" self._release_page_url = f"{base_url}/{owner}/{repo}/releases/tag" self._timeout = timeout async def get_latest_release(self) -> Optional[ReleaseInfo]: """Fetch the latest stable release from Gitea API. Returns: ReleaseInfo for the latest non-prerelease, or None on failure. """ import asyncio try: data = await asyncio.to_thread(self._fetch_releases) except Exception as e: logger.warning("Failed to check for updates: %s", e) return None if not data: return None # Find the first non-prerelease, non-draft release for release in data: if release.get("draft") or release.get("prerelease"): continue tag = release.get("tag_name", "") version = tag.lstrip("v") if not version: continue return ReleaseInfo( version=version, url=f"{self._release_page_url}/{tag}", prerelease=False, ) logger.debug("No stable releases found") return None def _fetch_releases(self) -> list[dict]: """Synchronous HTTP fetch of releases (run in thread).""" url = f"{self._api_url}?limit=5" req = urllib.request.Request(url, headers={"Accept": "application/json"}) try: with urllib.request.urlopen(req, timeout=self._timeout) as resp: return json.loads(resp.read().decode()) except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError, OSError) as e: raise RuntimeError(f"Gitea API request failed: {e}") from e