from quart import g, jsonify, request from http import HTTPStatus import base64 from lnbits.core.crud import get_wallet from lnbits.core.services import create_invoice, check_invoice_status from lnbits.decorators import api_check_wallet_key, api_validate_post_request import httpx from . import jukebox_ext from .crud import ( create_jukebox, update_jukebox, get_jukebox, get_jukeboxs, delete_jukebox, create_jukebox_payment, get_jukebox_payment, update_jukebox_payment, ) from lnbits.core.services import create_invoice, check_invoice_status @jukebox_ext.route("/api/v1/jukebox", methods=["GET"]) @api_check_wallet_key("invoice") async def api_get_jukeboxs(): try: return ( jsonify( [{**jukebox._asdict()} for jukebox in await get_jukeboxs(g.wallet.user)] ), HTTPStatus.OK, ) except: return "", HTTPStatus.NO_CONTENT ##################SPOTIFY AUTH##################### @jukebox_ext.route("/api/v1/jukebox/spotify/cb/", methods=["GET"]) async def api_check_credentials_callbac(juke_id): sp_code = "" sp_access_token = "" sp_refresh_token = "" try: jukebox = await get_jukebox(juke_id) except: return ( jsonify({"error": "No Jukebox"}), HTTPStatus.FORBIDDEN, ) if request.args.get("code"): sp_code = request.args.get("code") jukebox = await update_jukebox( juke_id=juke_id, sp_secret=jukebox.sp_secret, sp_access_token=sp_code ) if request.args.get("access_token"): sp_access_token = request.args.get("access_token") sp_refresh_token = request.args.get("refresh_token") jukebox = await update_jukebox( juke_id=juke_id, sp_secret=jukebox.sp_secret, sp_access_token=sp_access_token, sp_refresh_token=sp_refresh_token, ) return "

Success!

You can close this window

" @jukebox_ext.route("/api/v1/jukebox/spotify/", methods=["GET"]) @api_check_wallet_key("invoice") async def api_check_credentials_check(sp_id): jukebox = await get_jukebox(sp_id) return jsonify(jukebox._asdict()), HTTPStatus.CREATED @jukebox_ext.route("/api/v1/jukebox/", methods=["POST"]) @jukebox_ext.route("/api/v1/jukebox/", methods=["PUT"]) @api_check_wallet_key("admin") @api_validate_post_request( schema={ "user": {"type": "string", "empty": False, "required": True}, "title": {"type": "string", "empty": False, "required": True}, "wallet": {"type": "string", "empty": False, "required": True}, "sp_user": {"type": "string", "empty": False, "required": True}, "sp_secret": {"type": "string", "required": True}, "sp_access_token": {"type": "string", "required": False}, "sp_refresh_token": {"type": "string", "required": False}, "sp_device": {"type": "string", "required": False}, "sp_playlists": {"type": "string", "required": False}, "price": {"type": "string", "required": True}, } ) async def api_create_update_jukebox(juke_id=None): if juke_id: jukebox = await update_jukebox(juke_id=juke_id, inkey=g.wallet.inkey, **g.data) else: jukebox = await create_jukebox(inkey=g.wallet.inkey, **g.data) return jsonify(jukebox._asdict()), HTTPStatus.CREATED @jukebox_ext.route("/api/v1/jukebox/", methods=["DELETE"]) @api_check_wallet_key("admin") async def api_delete_item(juke_id): await delete_jukebox(juke_id) try: return ( jsonify( [{**jukebox._asdict()} for jukebox in await get_jukeboxs(g.wallet.user)] ), HTTPStatus.OK, ) except: return "", HTTPStatus.NO_CONTENT ################JUKEBOX ENDPOINTS################## ######GET ACCESS TOKEN###### @jukebox_ext.route( "/api/v1/jukebox/jb/playlist//", methods=["GET"] ) async def api_get_jukebox_song(juke_id, sp_playlist, retry=False): try: jukebox = await get_jukebox(juke_id) except: return ( jsonify({"error": "No Jukebox"}), HTTPStatus.FORBIDDEN, ) tracks = [] async with httpx.AsyncClient() as client: try: r = await client.get( "https://api.spotify.com/v1/playlists/" + sp_playlist + "/tracks", timeout=40, headers={"Authorization": "Bearer " + jukebox.sp_access_token}, ) if "items" not in r.json(): if r.status_code == 401: token = await api_get_token(juke_id) if token == False: return False elif retry: return ( jsonify({"error": "Failed to get auth"}), HTTPStatus.FORBIDDEN, ) else: return await api_get_jukebox_song( juke_id, sp_playlist, retry=True ) return r, HTTPStatus.OK for item in r.json()["items"]: tracks.append( { "id": item["track"]["id"], "name": item["track"]["name"], "album": item["track"]["album"]["name"], "artist": item["track"]["artists"][0]["name"], "image": item["track"]["album"]["images"][0]["url"], } ) except AssertionError: something = None return jsonify([track for track in tracks]) async def api_get_token(juke_id): try: jukebox = await get_jukebox(juke_id) except: return ( jsonify({"error": "No Jukebox"}), HTTPStatus.FORBIDDEN, ) async with httpx.AsyncClient() as client: try: r = await client.post( "https://accounts.spotify.com/api/token", timeout=40, params={ "grant_type": "refresh_token", "refresh_token": jukebox.sp_refresh_token, "client_id": jukebox.sp_user, }, headers={ "Content-Type": "application/x-www-form-urlencoded", "Authorization": "Basic " + base64.b64encode( str(jukebox.sp_user + ":" + jukebox.sp_secret).encode("ascii") ).decode("ascii"), "Content-Type": "application/x-www-form-urlencoded", }, ) if "access_token" not in r.json(): return False else: await update_jukebox( juke_id=juke_id, sp_access_token=r.json()["access_token"] ) except AssertionError: something = None return True ######CHECK DEVICE @jukebox_ext.route("/api/v1/jukebox/jb/", methods=["GET"]) async def api_get_jukebox_device_check(juke_id, retry=False): try: jukebox = await get_jukebox(juke_id) except: return ( jsonify({"error": "No Jukebox"}), HTTPStatus.FORBIDDEN, ) async with httpx.AsyncClient() as client: rDevice = await client.get( "https://api.spotify.com/v1/me/player/devices", timeout=40, headers={"Authorization": "Bearer " + jukebox.sp_access_token}, ) if rDevice.status_code == 204 or rDevice.status_code == 200: return ( rDevice, HTTPStatus.OK, ) elif rDevice.status_code == 401 or rDevice.status_code == 403: token = await api_get_token(juke_id) if token == False: return ( jsonify({"error": "No device connected"}), HTTPStatus.FORBIDDEN, ) elif retry: return ( jsonify({"error": "Failed to get auth"}), HTTPStatus.FORBIDDEN, ) else: return api_get_jukebox_device_check(juke_id, retry=True) else: return ( jsonify({"error": "No device connected"}), HTTPStatus.FORBIDDEN, ) ######GET INVOICE STUFF @jukebox_ext.route("/api/v1/jukebox/jb/invoice//", methods=["GET"]) async def api_get_jukebox_invoice(juke_id, song_id): try: jukebox = await get_jukebox(juke_id) except: return ( jsonify({"error": "No Jukebox"}), HTTPStatus.FORBIDDEN, ) invoice = await create_invoice( wallet_id=jukebox.wallet, amount=jukebox.price, memo=jukebox.title, extra={"tag": "jukebox"}, ) jukebox_payment = await create_jukebox_payment(song_id, invoice[0], juke_id) return jsonify(invoice, jukebox_payment) @jukebox_ext.route( "/api/v1/jukebox/jb/checkinvoice//", methods=["GET"] ) async def api_get_jukebox_invoice_check(pay_hash, juke_id): try: jukebox = await get_jukebox(juke_id) except: return ( jsonify({"error": "No Jukebox"}), HTTPStatus.FORBIDDEN, ) try: status = await check_invoice_status(jukebox.wallet, pay_hash) is_paid = not status.pending except Exception as exc: return jsonify({"paid": False}), HTTPStatus.OK if is_paid: wallet = await get_wallet(jukebox.wallet) payment = await wallet.get_payment(pay_hash) await payment.set_pending(False) await update_jukebox_payment(pay_hash, paid=True) return jsonify({"paid": True}), HTTPStatus.OK return jsonify({"paid": False}), HTTPStatus.OK @jukebox_ext.route( "/api/v1/jukebox/jb/invoicep///", methods=["GET"] ) async def api_get_jukebox_invoice_paid(song_id, juke_id, pay_hash, retry=False): try: jukebox = await get_jukebox(juke_id) except: return ( jsonify({"error": "No Jukebox"}), HTTPStatus.FORBIDDEN, ) jukebox_payment = await get_jukebox_payment(pay_hash) if jukebox_payment.paid: async with httpx.AsyncClient() as client: r = await client.get( "https://api.spotify.com/v1/me/player/currently-playing?market=ES", timeout=40, headers={"Authorization": "Bearer " + jukebox.sp_access_token}, ) rDevice = await client.get( "https://api.spotify.com/v1/me/player", timeout=40, headers={"Authorization": "Bearer " + jukebox.sp_access_token}, ) isPlaying = False if rDevice.status_code == 200: isPlaying = rDevice.json()["is_playing"] if r.status_code == 204 or isPlaying == False: async with httpx.AsyncClient() as client: uri = ["spotify:track:" + song_id] r = await client.put( "https://api.spotify.com/v1/me/player/play?device_id=" + jukebox.sp_device.split("-")[1], json={"uris": uri}, timeout=40, headers={"Authorization": "Bearer " + jukebox.sp_access_token}, ) if r.status_code == 204: return jsonify(jukebox_payment), HTTPStatus.OK elif r.status_code == 401 or r.status_code == 403: token = await api_get_token(juke_id) if token == False: return ( jsonify({"error": "Invoice not paid"}), HTTPStatus.FORBIDDEN, ) elif retry: return ( jsonify({"error": "Failed to get auth"}), HTTPStatus.FORBIDDEN, ) else: return api_get_jukebox_invoice_paid( song_id, juke_id, pay_hash, retry=True ) else: return ( jsonify({"error": "Invoice not paid"}), HTTPStatus.FORBIDDEN, ) elif r.status_code == 200: async with httpx.AsyncClient() as client: r = await client.post( "https://api.spotify.com/v1/me/player/queue?uri=spotify%3Atrack%3A" + song_id + "&device_id=" + jukebox.sp_device.split("-")[1], timeout=40, headers={"Authorization": "Bearer " + jukebox.sp_access_token}, ) if r.status_code == 204: return jsonify(jukebox_payment), HTTPStatus.OK elif r.status_code == 401 or r.status_code == 403: token = await api_get_token(juke_id) if token == False: return ( jsonify({"error": "Invoice not paid"}), HTTPStatus.OK, ) elif retry: return ( jsonify({"error": "Failed to get auth"}), HTTPStatus.FORBIDDEN, ) else: return await api_get_jukebox_invoice_paid( song_id, juke_id, pay_hash ) else: return ( jsonify({"error": "Invoice not paid"}), HTTPStatus.OK, ) elif r.status_code == 401 or r.status_code == 403: token = await api_get_token(juke_id) if token == False: return ( jsonify({"error": "Invoice not paid"}), HTTPStatus.OK, ) elif retry: return ( jsonify({"error": "Failed to get auth"}), HTTPStatus.FORBIDDEN, ) else: return await api_get_jukebox_invoice_paid( song_id, juke_id, pay_hash ) return jsonify({"error": "Invoice not paid"}), HTTPStatus.OK ############################GET TRACKS @jukebox_ext.route("/api/v1/jukebox/jb/currently/", methods=["GET"]) async def api_get_jukebox_currently(juke_id, retry=False): try: jukebox = await get_jukebox(juke_id) except: return ( jsonify({"error": "No Jukebox"}), HTTPStatus.FORBIDDEN, ) async with httpx.AsyncClient() as client: try: r = await client.get( "https://api.spotify.com/v1/me/player/currently-playing?market=ES", timeout=40, headers={"Authorization": "Bearer " + jukebox.sp_access_token}, ) if r.status_code == 204: return jsonify({"error": "Nothing"}), HTTPStatus.OK elif r.status_code == 200: try: response = r.json() track = { "id": response["item"]["id"], "name": response["item"]["name"], "album": response["item"]["album"]["name"], "artist": response["item"]["artists"][0]["name"], "image": response["item"]["album"]["images"][0]["url"], } return jsonify(track), HTTPStatus.OK except: return jsonify("Something went wrong"), HTTPStatus.NOT_FOUND elif r.status_code == 401: token = await api_get_token(juke_id) if token == False: return ( jsonify({"error": "Invoice not paid"}), HTTPStatus.FORBIDDEN, ) elif retry: return ( jsonify({"error": "Failed to get auth"}), HTTPStatus.FORBIDDEN, ) else: return await api_get_jukebox_currently(juke_id, retry=True) else: return jsonify("Something went wrong"), HTTPStatus.NOT_FOUND except AssertionError: return jsonify("Something went wrong"), HTTPStatus.NOT_FOUND