''' rarbg api → rss https://torrentapi.org/apidocs_v2.txt ''' import asyncio from datetime import datetime, timedelta from email.utils import formatdate from urllib.parse import parse_qs from aiohttp import get, web from dateutil import parser from humanize import naturalsize from jinja2 import Template API_ENDPOINT = 'https://torrentapi.org/pubapi_v2.php' API_RATE_LIMIT = timedelta(seconds=2) TOKEN_LIFESPAN = timedelta(minutes=15) TEMPLATE = Template(''' {{title}} https://torrentapi.org/apidocs_v2.txt 3600 {% for entry in entries %} {{entry.title}} ({{entry.hsize}}) {{entry.hash}} {{entry.pubdate}} {% endfor %} ''') app = web.Application() app.token = None app.token_got = datetime.now() class RateLimit: def __init__(self, rate_limit): self.rate_limit = rate_limit self.next_call = datetime.now() async def __aenter__(self): now = datetime.now() self.next_call = max(now, self.next_call) + self.rate_limit sleep = self.next_call - now - self.rate_limit print('sleep', str(sleep)) await asyncio.sleep(sleep.total_seconds()) async def __aexit__(self, exc_type, exc, tb): pass api_rate_limit = RateLimit(API_RATE_LIMIT) async def update_token(): token_expired = datetime.now() > app.token_got + TOKEN_LIFESPAN if not app.token or token_expired: resp = await get(API_ENDPOINT, params={'get_token': 'get_token'}) data = await resp.json() app.token = data['token'] app.token_got = datetime.now() async def api(params): print('request', params) await update_token() params.update(token=app.token, format='json_extended') async with api_rate_limit: resp = await get(API_ENDPOINT, params=params) data = await resp.json() if 'error' in data: print('too many requests') return web.HTTPServiceUnavailable(text=data['error']) for i in data['torrent_results']: i.update( pubdate=formatdate(parser.parse(i['pubdate']).timestamp()), hsize=naturalsize(i['size'], gnu=True), hash=parse_qs(i['download'])['magnet:?xt'][0].split(':')[-1], ) print('response', params) result = TEMPLATE.render(title='rarbg', entries=data['torrent_results']) return web.Response(text=result) async def rarbg_rss(request): params = dict(request.GET) if 'string' in request.match_info: params.update(mode='search', search_string=request.match_info['string']) if 'imdb' in request.match_info: params.update(mode='search', search_imdb=request.match_info['imdb']) if 'tvdb' in request.match_info: params.update(mode='search', search_tvdb=request.match_info['tvdb']) return await api(params) app.router.add_route('GET', '/', rarbg_rss) app.router.add_route('GET', '/search/{string}', rarbg_rss) app.router.add_route('GET', '/imdb/{imdb}', rarbg_rss) app.router.add_route('GET', '/tvdb/{tvdb}', rarbg_rss) def main(): loop = asyncio.get_event_loop() handler = app.make_handler() f = loop.create_server(handler, '0.0.0.0', 8080) srv = loop.run_until_complete(f) print('serving on', srv.sockets[0].getsockname()) loop.run_forever() if __name__ == '__main__': main()