From 54344c985b0998270a2c372b6819ccb2c25cf301 Mon Sep 17 00:00:00 2001 From: Robert Dargavel Smith Date: Tue, 24 Aug 2021 13:20:48 +0100 Subject: [PATCH] serve widgets from backend, cache in frontend --- backend/main.py | 26 +++++++++-- src/App.js | 1 + src/components/Banner.js | 9 +++- src/components/Playlist.js | 8 +++- src/components/RemovableTrack.js | 12 ++--- src/components/Track.js | 80 +++++++++++++++++++------------- src/components/TrackSelector.js | 16 ++++--- src/service-worker.js | 22 +++++++-- 8 files changed, 118 insertions(+), 56 deletions(-) diff --git a/backend/main.py b/backend/main.py index 5d1bad7..cfe4f96 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,6 +1,5 @@ import os import re -import base64 import urllib import aiohttp from . import models @@ -9,6 +8,7 @@ from .deejai import DeejAI from typing import Optional from sqlalchemy import desc +from base64 import b64encode from starlette.types import Scope from sqlalchemy.orm import Session from starlette.responses import Response @@ -79,7 +79,7 @@ async def spotify_callback(code: str, state: Optional[str] = '/'): headers = { 'Authorization': 'Basic ' + - base64.b64encode(f'{credentials.client_id}:{credentials.client_secret}' + b64encode(f'{credentials.client_id}:{credentials.client_secret}' .encode('utf-8')).decode('utf-8') } async with aiohttp.ClientSession() as session: @@ -108,7 +108,7 @@ async def spotify_refresh_token(refresh_token: str): headers = { 'Authorization': 'Basic ' + - base64.b64encode(f'{credentials.client_id}:{credentials.client_secret}' + b64encode(f'{credentials.client_id}:{credentials.client_secret}' .encode('utf-8')).decode('utf-8') } async with aiohttp.ClientSession() as session: @@ -125,6 +125,26 @@ async def spotify_refresh_token(refresh_token: str): return json +@app.get("/api/v1/widget") +async def widget(track_id: str): + headers = { + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36' + } + async with aiohttp.ClientSession() as session: + try: + async with session.get( + f'https://open.spotify.com/embed/track/{track_id}', + headers=headers) as response: + if response.status != 200: + raise HTTPException(status_code=response.status, + detail=response.reason) + text = await response.text() + except aiohttp.ClientError as error: + raise HTTPException(status_code=400, detail=str(error)) + return b64encode(text.encode('ascii')) + + @app.get("/api/v1/search") async def search_tracks(string: str, max_items: int): ids = await deejai.search(string, max_items) diff --git a/src/App.js b/src/App.js index 54d9546..b392b8c 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,7 @@ // TODO // // frontend: +// robots.txt etc // unit tests // banner image and ico file // fix warnings for unique key diff --git a/src/components/Banner.js b/src/components/Banner.js index 064c9f9..bbdb6a2 100644 --- a/src/components/Banner.js +++ b/src/components/Banner.js @@ -12,7 +12,14 @@ export default function Banner({ loggedIn = false, onSelect = f => f }) { return ( <> - +
diff --git a/src/components/Playlist.js b/src/components/Playlist.js index 512cc1c..8f7fafb 100644 --- a/src/components/Playlist.js +++ b/src/components/Playlist.js @@ -4,8 +4,12 @@ export default function Playlist({ track_ids = [], waypoints = [] }) { return ( <> {track_ids.map((track_id, i) => ( -
- = 0 } /> +
+ = 0} + />
))} diff --git a/src/components/RemovableTrack.js b/src/components/RemovableTrack.js index acaa71c..4171c66 100644 --- a/src/components/RemovableTrack.js +++ b/src/components/RemovableTrack.js @@ -15,13 +15,11 @@ export default function RemovableTrack({ track_id, uuid, onRemove = f => f }) { > - {loaded ? - onRemove(uuid)} - /> : <> - } + onRemove(uuid)} + /> ); } diff --git a/src/components/Track.js b/src/components/Track.js index 3e87b1a..66cf6f9 100644 --- a/src/components/Track.js +++ b/src/components/Track.js @@ -1,51 +1,65 @@ -import { useState } from 'react'; +import { Suspense } from 'react'; import Spinner from 'react-bootstrap/Spinner'; import './Track.css'; -function SpotifyTrackWidget({ track_id, highlight, loaded, onLoad = f => f }) { +function createResource(pending) { + let error, response; + pending.then(r => response = r).catch(e => error = e); + return { + read() { + if (error) throw error; + if (response) return response; + throw pending; + } + }; +} + +function SpotifyTrackWidget({ track_id, highlight, resource }) { + const iframe = (resource) ? resource.read() : null; + return (