mental32 / spotify.py

🌐 API wrapper for Spotify 🎶
https://spotifypy.readthedocs.io/en/latest/
MIT License
150 stars 38 forks source link

Example with an async http server #46

Open AdrienPensart opened 4 years ago

AdrienPensart commented 4 years ago

Do you see an interest having examples with an async http server for authentication part ?

I struggle to have a working example with sanic but work is in progress.

frafra commented 4 years ago

Here is an example I wrote:

#!/usr/bin/env python3

from aiohttp import web
import os
import spotify

CLIENT_ID = os.environ['CLIENT_ID']
CLIENT_SECRET = os.environ['CLIENT_SECRET']
REDIRECT_URI = os.environ['REDIRECT_URI']

async def auth(request):
    async with spotify.Client(CLIENT_ID, CLIENT_SECRET) as client:
        url = client.oauth2_url(
            redirect_uri=REDIRECT_URI,
            scopes=['user-library-read'],
        )
        return web.HTTPFound(url)

async def callback(request):
    async with spotify.Client(CLIENT_ID, CLIENT_SECRET) as client:
        user = await spotify.User.from_code(
            client=client,
            code=request.query['code'],
            redirect_uri=REDIRECT_URI,
            )
    tracks = await user.library.get_all_tracks()
    return web.HTTPFound(repr(tracks))

app = web.Application()
app.add_routes([
    web.get('/', auth),
    web.get('/callback', callback),
])

if __name__ == '__main__':
    web.run_app(app)
mental32 commented 4 years ago

Actually I wouldn't recommend that @frafra spotify.Client is made to me created once and carried around for the lifetime of the program. So creating and destroying instances on every route handler is a very expensive thing to do.

I'd suggesting using the contextvars module:

import asyncio
from contextvars import ContextVar

from aiohttp.web import AppRunner, TCPSite

CLIENT: ContextVar("spotify_client")

async def callback(request):
    client = CLIENT.get()
    user = await spotify.User.from_code(
        client=client,
        code=request.query['code'],
         redirect_uri=REDIRECT_URI,
    )
    tracks = await user.library.get_all_tracks()
    return web.HTTPFound(repr(tracks))

async def main() -> None:
    async with spotify.Client(CLIENT_ID, CLIENT_SECRET) as client:
        CLIENT.set(client)
        runner = AppRunner(app)
        await runner.setup()
        site = TCPSite(runner, 'localhost', 8080)
        await site.start()

if __name__ == "__main__":
    asyncio.run(main())
frafra commented 4 years ago

Using ContextVar is a good suggestion indeed :-) The code looks incomplete to me now, as the auth part with the initial request and scopes is missing.

mental32 commented 4 years ago

@frafra It was my intention that the codeblock I posted be combined with your one haha

would anyone like to PR this in themselves? Don't worry if not, I can add this is either way :)