bootstrap-sona/server.py
2025-03-16 17:48:50 -07:00

112 lines
No EOL
5.7 KiB
Python

#!/bin/env python3
import requests, traceback
from urllib.parse import urlparse, urlunparse, quote
from flask import Flask, redirect, abort, render_template, request, make_response, send_file
app = Flask(__name__)
def createSpecial(url):
temp = [url, url]
temp[0] = "/".join(temp[0].split("/")[:3]) + "/.well-known/fursona"
temp[1] = "/".join(temp[0].split("/")[:3]) + "/.well-known/fursona.json"
return temp
@app.errorhandler(404)
def notfound(e):
theme = request.cookies.get('theme') # The current theme
return render_template("error.html", message=f"Error 404 - You seem lost.", theme=theme), 404
@app.errorhandler(500)
def notfound(e):
theme = request.cookies.get('theme') # The current theme
return render_template("error.html", message=f"Error 500 - Went kaboom.", theme=theme), 500
@app.route("/logo.svg")
def logo_svg():
return send_file("logo.svg")
@app.route("/fallback.svg")
def fallback_avatar():
return send_file("fallback.svg")
@app.route("/logo.png")
def logo_png():
return send_file("logo.png")
@app.route("/favicon.ico")
def favicon_logo_bind():
return logo_png()
@app.route("/")
def home():
theme = request.cookies.get('theme') # The current theme
if theme == None:
theme = "light" # Fallback
return render_template("home.html", theme=theme, color="#000000" if theme == "light" else "#FFFFFF")
@app.route("/switch")
def switchTheme():
theme = request.cookies.get('theme') # The current theme
resp = make_response(redirect("/"))
resp.set_cookie("theme", "dark" if theme in ["light", None] else "light")
return resp
@app.route("/search")
def search():
theme = request.cookies.get('theme') # The current theme
url_raw = request.args.get('url')
if url_raw in [None, ""]:
return redirect("/")
url = urlparse(url_raw)
if url.scheme in ["http", "https"]:
try:
html = f'<!DOCTYPE html><html lang="en-us"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="View the fursona(s) in {url.hostname}"><link href="//cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><title>{url.hostname} - Bootstrap \'Sona</title></head><body data-bs-theme="{theme}"><center>'
stat_urls = [ # Grab some URLs to try
url_raw,
*createSpecial(url_raw)
]
ok = False
for uri in stat_urls:
req = requests.get(uri)
try:
res = req.json() # Pass if JSON parses correctly
ok2 = True
except:
ok2 = False
if req.ok and ok2: # If status code is OK too
ok = True
break
if not ok:
return render_template("error.html", message="I was unable to find a correct URL for this site, does a fursona.json exist?\nall URLs attempted:\n" + "\n".join(stat_urls), theme=theme), 404
if not "sonas" in res: # If sonas doesn't exist in the JSON file (shown in schema)
return render_template("error.html", message="This site has a fursona file, but the JSON doesn't have a \"sonas\" property.", theme=theme), 500
for sona in res["sonas"]:
# Avatar image
img = f'<img src="{sona["avatar"]}" alt="{sona["avatarAlt"] if "avatarAlt" in sona else "No alt text"}" title="{sona["avatarAlt"] if "avatarAlt" in sona else "No alt text"}" class="card-img-top">' if "avatar" in sona else f'<img src="/fallback.svg" alt="NO AVATAR" title="NO AVATAR" class="card-img-top">'
if "avatarAttribution" in sona:
img += f'<div class="card-body text-muted">{sona["avatarAttribution"]}</div>'
# Colors
if not "colors" in sona:
colors = ""
else:
colors = ""
for color in sona["colors"]:
# TODO: Replace the info icons with bootstrap-sona logos
colors += f'<svg width="24px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path fill="{color}" d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg> {color}<br><br>'
# Reference sheet (link)
ref = f'<a href="{sona["ref"]}" title="{sona["refAlt"] if "refAlt" in sona else "No alt text"}" class="btn btn-primary">Reference sheet</a>' if "ref" in sona else ""
# Age
age = f'<p>Age: {sona["age"]}</p>' if "age" in sona else ""
# Everything else.
html += f'<div class="card" style="width: 24rem;">{img}<div class="card-body"><h5 class="card-title">{sona["name"]}</h5><p class="card-text text-muted">{sona["species"]} &#8729 {sona["gender"]} &#8729 {sona["pronouns"]}</p><p class="card-text">{sona["description"]}</p>{age}{colors}{ref}</div></div>'
return html + "<hr><a href=\"/\">Back</a></center></body></html>"
except:
return render_template("error.html", message=traceback.format_exc(), theme=theme), 500
else:
return render_template("error.html", message=f"Unrecognised URL sheme {url.scheme}", theme=theme), 500
# If not called by a WSGI/ASGI framework...
if __name__ == '__main__':
# ...Run the app with Flask's development server
app.run(host='127.0.0.1',port=2011)