diff --git a/lnbits/extensions/jukebox/templates/jukebox/error.html b/lnbits/extensions/jukebox/templates/jukebox/error.html new file mode 100644 index 000000000..746e403cd --- /dev/null +++ b/lnbits/extensions/jukebox/templates/jukebox/error.html @@ -0,0 +1,35 @@ +{% extends "public.html" %} {% block page %} +
+
+ + +
+

Jukebox error

+
+ + +
Ask the host to turn on the device and launch spotify
+
+
+
+
+
+ + {% endblock %} {% block scripts %} + + + + {% endblock %} +
diff --git a/lnbits/extensions/jukebox/templates/jukebox/jukebox.html b/lnbits/extensions/jukebox/templates/jukebox/jukebox.html index bd33cbdd0..b8e65bbc1 100644 --- a/lnbits/extensions/jukebox/templates/jukebox/jukebox.html +++ b/lnbits/extensions/jukebox/templates/jukebox/jukebox.html @@ -170,7 +170,7 @@ 'Success! "' + self.receive.name + '" will be played soon', timeout: 3000 }) - self.getCurrent() + self.paid = false response1 = [] } @@ -183,10 +183,12 @@ }) } }, 4000) + }) .catch(err => { LNbits.utils.notifyApiError(err) }) + self.getCurrent() }, checkInvoice(juke_id, paymentHash) { @@ -219,6 +221,7 @@ .catch(function (error) { LNbits.utils.notifyApiError(error) }) + }, selectPlaylist() { self = this diff --git a/lnbits/extensions/jukebox/views.py b/lnbits/extensions/jukebox/views.py index 665c68219..39bab6a21 100644 --- a/lnbits/extensions/jukebox/views.py +++ b/lnbits/extensions/jukebox/views.py @@ -10,6 +10,7 @@ from functools import wraps import json from . import jukebox_ext from .crud import get_jukebox +from .views_api import api_get_jukebox_device_check from urllib.parse import unquote @@ -25,11 +26,15 @@ async def print_qr_codes(juke_id): jukebox = await get_jukebox(juke_id) if not jukebox: return "error" - - return await render_template( - "jukebox/jukebox.html", - playlists=jukebox.sp_playlists.split(","), - juke_id=juke_id, - price=jukebox.price, - inkey=jukebox.inkey, - ) + device = await api_get_jukebox_device_check(juke_id) + devices = json.loads(device[0].text) + if len(devices["devices"]) > 0: + return await render_template( + "jukebox/jukebox.html", + playlists=jukebox.sp_playlists.split(","), + juke_id=juke_id, + price=jukebox.price, + inkey=jukebox.inkey, + ) + else: + return await render_template("jukebox/error.html") diff --git a/lnbits/extensions/jukebox/views_api.py b/lnbits/extensions/jukebox/views_api.py index 29799acb4..1a142fb9a 100644 --- a/lnbits/extensions/jukebox/views_api.py +++ b/lnbits/extensions/jukebox/views_api.py @@ -48,7 +48,13 @@ async def api_check_credentials_callbac(juke_id): sp_code = "" sp_access_token = "" sp_refresh_token = "" - jukebox = await get_jukebox(juke_id) + 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( @@ -122,8 +128,14 @@ async def api_delete_item(juke_id): @jukebox_ext.route( "/api/v1/jukebox/jb/playlist//", methods=["GET"] ) -async def api_get_jukebox_song(juke_id, sp_playlist): - jukebox = await get_jukebox(juke_id) +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: @@ -137,8 +149,15 @@ async def api_get_jukebox_song(juke_id, sp_playlist): 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) + return await api_get_jukebox_song( + juke_id, sp_playlist, retry=True + ) return r, HTTPStatus.OK for item in r.json()["items"]: tracks.append( @@ -156,7 +175,13 @@ async def api_get_jukebox_song(juke_id, sp_playlist): async def api_get_token(juke_id): - jukebox = await get_jukebox(juke_id) + try: + jukebox = await get_jukebox(juke_id) + except: + return ( + jsonify({"error": "No Jukebox"}), + HTTPStatus.FORBIDDEN, + ) async with httpx.AsyncClient() as client: try: @@ -188,12 +213,63 @@ async def api_get_token(juke_id): 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): - jukebox = await get_jukebox(juke_id) + try: + jukebox = await get_jukebox(juke_id) + except: + return ( + jsonify({"error": "No Jukebox"}), + HTTPStatus.FORBIDDEN, + ) invoice = await create_invoice( wallet_id=jukebox.wallet, @@ -211,7 +287,13 @@ async def api_get_jukebox_invoice(juke_id, song_id): "/api/v1/jukebox/jb/checkinvoice//", methods=["GET"] ) async def api_get_jukebox_invoice_check(pay_hash, juke_id): - jukebox = await get_jukebox(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 @@ -229,8 +311,14 @@ async def api_get_jukebox_invoice_check(pay_hash, juke_id): @jukebox_ext.route( "/api/v1/jukebox/jb/invoicep///", methods=["GET"] ) -async def api_get_jukebox_invoice_paid(song_id, juke_id, pay_hash): - jukebox = await get_jukebox(juke_id) +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: @@ -239,7 +327,16 @@ async def api_get_jukebox_invoice_paid(song_id, juke_id, pay_hash): timeout=40, headers={"Authorization": "Bearer " + jukebox.sp_access_token}, ) - if r.status_code == 204: + 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( @@ -258,9 +355,14 @@ async def api_get_jukebox_invoice_paid(song_id, juke_id, pay_hash): 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 + song_id, juke_id, pay_hash, retry=True ) else: return ( @@ -287,6 +389,11 @@ async def api_get_jukebox_invoice_paid(song_id, juke_id, pay_hash): 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 @@ -303,6 +410,11 @@ async def api_get_jukebox_invoice_paid(song_id, juke_id, pay_hash): 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 @@ -314,8 +426,14 @@ async def api_get_jukebox_invoice_paid(song_id, juke_id, pay_hash): @jukebox_ext.route("/api/v1/jukebox/jb/currently/", methods=["GET"]) -async def api_get_jukebox_currently(juke_id): - jukebox = await get_jukebox(juke_id) +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( @@ -326,16 +444,20 @@ async def api_get_jukebox_currently(juke_id): if r.status_code == 204: return jsonify({"error": "Nothing"}), HTTPStatus.OK elif r.status_code == 200: - response = r.json() - response["item"] - 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 track, HTTPStatus.OK + try: + response = r.json() + response["item"] + 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 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: @@ -343,8 +465,13 @@ async def api_get_jukebox_currently(juke_id): 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) + return await api_get_jukebox_currently(juke_id, retry=True) else: return jsonify("Something went wrong"), HTTPStatus.NOT_FOUND except AssertionError: