Dynamic readme

This commit is contained in:
novatorem
2020-08-15 22:22:39 -04:00
commit cf59dbeaf7
7 changed files with 405 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
venv
__pycache__
.cache*
env
.env

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
Aphelion | Perihelion
:-------------------------:|-------
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [![Spotify](https://novatorem.vercel.app/api/spotify-playing)](https://open.spotify.com/user/omnitenebris)<br>|<div style="text-align: justify">Software Developer currently working on cool projects at [Cast](https://blacktrax.cast-soft.com/showcase/). Everything here is under the MIT License, [info here](https://choosealicense.com/licenses/mit/)!</div><br><p align="center">[![Website](https://img.shields.io/badge/website-dev-2a8?style=flat-square&logo=safari&logoColor=white)](https://novac.dev)</center> [![E-Mail](https://img.shields.io/badge/email-reveal-369?style=flat-square&logo=gmail&logoColor=white)](https://mailhide.io/e/5ck1H)<br>[![Spotify](https://img.shields.io/badge/spotify-omni-1DB954?style=flat-square&logo=spotify&logoColor=white)](https://open.spotify.com/user/omnitenebris) [![Visits](https://badges.pufler.dev/visits/novatorem/novatorem?logo=GitHub&label=github%20visits&color=blue&logoColor=white&style=flat-square)](https://github.com/novatorem)</p>
[//]: <> (The `&nbsp;` is to have Aphelion take up more space)

44
SetUp.md Normal file
View File

@@ -0,0 +1,44 @@
# Set Up Guide
## Spotify
* Create a [Spotify Application](https://developer.spotify.com/dashboard/applications)
* Put aside:
* `Client ID`
* `Client Secret`
* Click on **Edit Settings**
* In **Redirect URIs**:
* Add `http://localhost/callback/`
## Refresh Token
* Navigate to the following URL:
```
https://accounts.spotify.com/authorize?client_id={SPOTIFY_CLIENT_ID}&response_type=code&scope=user-read-currently-playing,user-read-recently-played&redirect_uri=http://localhost/callback/
```
* After logging in, save the {CODE} portion of: `http://localhost/callback/?code={CODE}`
* Create a string combining `{SPOTIFY_CLIENT_ID}:{SPOTIFY_CLIENT_SECRET}` (e.g. `5n7o4v5a3t7o5r2e3m1:5a8n7d3r4e2w5n8o2v3a7c5`) and encode into [Base64](https://www.base64encode.org/).
* Then run a [curl command](https://httpie.org/run) in the form of:
```sh
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -H "Authorization: Basic {BASE64}" -d "grant_type=authorization_code&redirect_uri=http://localhost/callback/&code={CODE}" https://accounts.spotify.com/api/token
```
* Save the Refresh token
## Vercel
* Register on [Vercel](https://vercel.com/)
* Create project linked to your github repo
* Add System Variables:
* `https://vercel.com/<YourName>/<ProjectName>/settings/environment-variables`
* `SPOTIFY_REFRESH_TOKEN`
* `SPOTIFY_CLIENT_ID`
* `SPOTIFY_SECRET_ID`
* Deploy!

3
api/requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
flask==1.1.2
requests==2.24.0
python-dotenv==0.14.0

120
api/spotify-playing.py Normal file
View File

@@ -0,0 +1,120 @@
from flask import Flask, Response, jsonify, render_template
from base64 import b64encode
from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv())
import requests
import json
import os
import random
SPOTIFY_CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID")
SPOTIFY_SECRET_ID = os.getenv("SPOTIFY_SECRET_ID")
SPOTIFY_REFRESH_TOKEN = os.getenv("SPOTIFY_REFRESH_TOKEN")
# scope user-read-currently-playing/user-read-recently-played
SPOTIFY_URL_REFRESH_TOKEN = "https://accounts.spotify.com/api/token"
SPOTIFY_URL_NOW_PLAYING = "https://api.spotify.com/v1/me/player/currently-playing"
SPOTIFY_URL_RECENTLY_PLAY = "https://api.spotify.com/v1/me/player/recently-played?limit=10"
app = Flask(__name__)
def getAuth():
return b64encode(f"{SPOTIFY_CLIENT_ID}:{SPOTIFY_SECRET_ID}".encode()).decode("ascii")
def refreshToken():
data = {
"grant_type": "refresh_token",
"refresh_token": SPOTIFY_REFRESH_TOKEN,
}
headers = {"Authorization": "Basic {}".format(getAuth())}
response = requests.post(SPOTIFY_URL_REFRESH_TOKEN, data=data, headers=headers)
return response.json()["access_token"]
def recentlyPlayed():
token = refreshToken()
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(SPOTIFY_URL_RECENTLY_PLAY, headers=headers)
if response.status_code == 204:
return {}
return response.json()
def nowPlaying():
token = refreshToken()
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(SPOTIFY_URL_NOW_PLAYING, headers=headers)
if response.status_code == 204:
return {}
return response.json()
def barGen(barCount):
barCSS = ""
left = 1
for i in range(1, barCount + 1):
anim = random.randint(1000, 1350)
barCSS += ".bar:nth-child({}) {{ left: {}px; animation-duration: {}ms; }}".format(
i, left, anim
)
left += 4
return barCSS
def loadImageB64(url):
resposne = requests.get(url)
return b64encode(resposne.content).decode("ascii")
def makeSVG(data):
barCount = 85
contentBar = "".join(["<div class='bar'></div>" for i in range(barCount)])
barCSS = barGen(barCount)
if data == {}:
content_bar = ""
recent_plays = recentlyPlayed()
size_recent_play = len(recent_plays["items"])
idx = random.randint(0, size_recent_play - 1)
item = recent_plays["items"][idx]["track"]
else:
item = data["item"]
img = loadImageB64(item["album"]["images"][1]["url"])
artistName = item["artists"][0]["name"].replace("&", "&amp;")
songName = item["name"].replace("&", "&amp;")
dataDict = {
"content_bar": contentBar,
"css_bar": barCSS,
"artist_name": artistName,
"song_name": songName,
"img": img,
}
return render_template("spotify.html.j2", **dataDict)
@app.route("/", defaults={"path": ""})
@app.route("/<path:path>")
def catch_all(path):
data = nowPlaying()
svg = makeSVG(data)
resp = Response(svg, mimetype="image/svg+xml")
resp.headers["Cache-Control"] = "s-maxage=1"
return resp
if __name__ == "__main__":
app.run(debug=True)

106
api/templates/preview.html Normal file
View File

@@ -0,0 +1,106 @@
<div xmlns="http://www.w3.org/1999/xhtml" class="container">
<style>
div {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
}
.main {
display: flex;
}
.art {
float: left;
width: 33.33%;
}
.container {
background-color: #121212;
border-radius: 10px;
padding: 10px 10px
}
.playing {
font-weight: bold;
color: #53b14f;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
.not-play {
color: #ff1616;
}
.artist {
font-weight: bold;
font-size: 24px;
color: #fff;
text-align: left;
margin-top: 5px;
}
.song {
font-size: 20px;
color: #b3b3b3;
text-align: left;
margin-top: 15px;
margin-bottom: 15px;
}
.logo {
margin-left: 5px;
margin-top: 5px;
}
.cover {
border-radius: 5px;
margin-top: 9px;
}
#bars {
height: 30px;
margin: -20px 0 0 0px;
position: absolute;
width: 40px;
}
.bar {
background: #53b14f;
bottom: 1px;
height: 3px;
position: absolute;
width: 3px;
animation: sound 0ms -800ms linear infinite alternate;
}
@keyframes sound {
0% {
opacity: .35;
height: 3px;
}
100% {
opacity: 1;
height: 28px;
}
}
</style>
<div class="main">
<a class="art" href="{}" target="_BLANK">
<center>
<img src="data:image/png;base64, {{img}}" width="200" height="200" class="cover" />
</center>
</a>
<div class="text">
<div class="artist">Artist</div>
<div class="song">Song</div>
<div id="bars"></div>
</div>
</div>
</div>

View File

@@ -0,0 +1,122 @@
<svg width="480" height="133" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<foreignObject width="480" height="133">
<div xmlns="http://www.w3.org/1999/xhtml" class="container">
<style>
div {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
}
.main {
display: flex;
}
.container {
border-radius: 5px;
padding: 10px 10px 10px 0px;
}
.playing {
font-weight: bold;
color: #53b14f;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
.not-play {
color: #ff1616;
}
.art {
float: left;
width: 27%;
margin-left: -5px;
}
.text {
width: 71%;
}
.song {
font-size: 24px;
color: #666;
text-align: center;
margin-top: 3px;
}
.artist {
font-size: 20px;
color: #b3b3b3;
text-align: center;
margin-bottom: 5px;
}
.logo {
margin-left: 5px;
margin-top: 5px;
}
.cover {
border-radius: 5px;
height: 100px;
width: 100px;
}
#bars {
height: 30px;
bottom: 23px;
margin: -20px 0 0 0px;
position: absolute;
width: 40px;
}
.bar {
background: #1DB954cc;
bottom: 1px;
height: 3px;
position: absolute;
width: 3px;
animation: sound 0ms -800ms linear infinite alternate;
}
@keyframes sound {
0% {
opacity: .35;
height: 3px;
}
100% {
opacity: 1;
height: 15px;
}
}
{{css_bar|safe}}
</style>
{% if song_name %}
<div class="main">
<a class="art" href="{}" target="_BLANK">
<center>
<img src="data:image/png;base64, {{img}}" class="cover" />
</center>
</a>
<div class="text">
<div class="song">{{song_name}}</div>
<div class="artist">{{artist_name}}</div>
<div id="bars">
{{content_bar|safe}}
</div>
</div>
{% else %}
<div class="playing not-play">Nothing playing on Spotify</div>
{% endif %}
</div>
</div>
</foreignObject>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB