mirror of
https://github.com/Alvin-Zilverstand/novatorem.git
synced 2026-04-17 14:48:08 +02:00
Dynamic readme
This commit is contained in:
3
api/requirements.txt
Normal file
3
api/requirements.txt
Normal 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
120
api/spotify-playing.py
Normal 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("&", "&")
|
||||
songName = item["name"].replace("&", "&")
|
||||
|
||||
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
106
api/templates/preview.html
Normal 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>
|
||||
122
api/templates/spotify.html.j2
Normal file
122
api/templates/spotify.html.j2
Normal 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 |
Reference in New Issue
Block a user