mirror of
https://github.com/revanced/revanced-releases-api.git
synced 2025-04-30 22:44:25 +02:00
117 lines
4.3 KiB
Python
117 lines
4.3 KiB
Python
import os
|
|
import pycmarkgfm
|
|
import httpx_cache
|
|
from typing import Dict, List
|
|
from base64 import b64decode
|
|
from bs4 import BeautifulSoup
|
|
|
|
class Releases:
|
|
|
|
"""Implements the methods required to get the latest releases and patches from revanced repositories."""
|
|
|
|
headers = {'Accept': "application/vnd.github+json",
|
|
'Authorization': "token " + os.environ['GITHUB_TOKEN']
|
|
}
|
|
|
|
async def get_release(self, client: httpx_cache.AsyncClient, repository: str) -> list:
|
|
"""Get assets from latest release in a given repository.
|
|
|
|
Args:
|
|
client (httpx_cache.AsyncClient): httpx_cache reusable async client
|
|
repository (str): Github's standard username/repository notation
|
|
|
|
Returns:
|
|
dict: dictionary of filename and download url
|
|
"""
|
|
assets = []
|
|
response = await client.get(f"https://api.github.com/repos/{repository}/releases/latest")
|
|
|
|
release_assets = response.json()['assets']
|
|
|
|
for asset in release_assets:
|
|
assets.append({ 'repository': repository,
|
|
'name': asset['name'],
|
|
'size': asset['size'],
|
|
'browser_download_url': asset['browser_download_url'],
|
|
'content_type': asset['content_type']
|
|
})
|
|
|
|
return assets
|
|
|
|
async def get_latest_releases(self, repositories: list) -> dict:
|
|
"""Runs get_release() in parallel for each repository.
|
|
|
|
Args:
|
|
repositories (list): List of repositories in Github's standard username/repository notation
|
|
|
|
Returns:
|
|
dict: A dictionary containing assets from each repository
|
|
"""
|
|
releases: Dict[str, List] = {}
|
|
releases['tools'] = []
|
|
|
|
async with httpx_cache.AsyncClient(headers=self.headers, http2=True) as client:
|
|
for repository in repositories:
|
|
files = await self.get_release(client, repository)
|
|
for file in files:
|
|
releases['tools'].append(file)
|
|
|
|
return releases
|
|
|
|
async def get_patches_readme(self, client: httpx_cache.AsyncClient) -> str:
|
|
"""Get revanced-patches repository's README.md.
|
|
|
|
Returns:
|
|
str: README.md content
|
|
"""
|
|
response = await client.get(f"https://api.github.com/repos/revanced/revanced-patches/contents/README.md")
|
|
|
|
return b64decode(response.json()['content']).decode('utf-8')
|
|
|
|
async def get_patchable_apps(self) -> dict:
|
|
"""Get patchable apps from revanced-patches repository.
|
|
|
|
Returns:
|
|
dict: Apps available for patching
|
|
"""
|
|
packages: Dict[str, List] = {}
|
|
packages['apps'] = []
|
|
|
|
async with httpx_cache.AsyncClient(headers=self.headers, http2=True) as client:
|
|
content = await self.get_patches_readme(client)
|
|
|
|
for line in content.splitlines():
|
|
if line.startswith(u'###'):
|
|
packages['apps'].append(line.split('`')[1])
|
|
|
|
return packages
|
|
|
|
async def get_latest_patches(self) -> dict:
|
|
"""Get latest patches from revanced-patches repository.
|
|
|
|
Returns:
|
|
dict: Patches available for a given app
|
|
"""
|
|
patches: Dict[str, List] = {}
|
|
patches['patches'] = []
|
|
|
|
async with httpx_cache.AsyncClient(headers=self.headers, http2=True) as client:
|
|
content = await self.get_patches_readme(client)
|
|
|
|
html = pycmarkgfm.gfm_to_html(content)
|
|
soup = BeautifulSoup(html, 'lxml')
|
|
|
|
headings = soup.find_all('h3')
|
|
|
|
for heading in headings:
|
|
app_name = heading.text.split(' ')[1]
|
|
for patch in heading.find_next_sibling().find_all('tr')[1:]:
|
|
app_patches = patch.find_all('td')
|
|
patches['patches'].append({"target_app": app_name,
|
|
"patch_name" : app_patches[0].text,
|
|
"description": app_patches[1].text,
|
|
"target_version": app_patches[2].text
|
|
})
|
|
|
|
return patches
|