adds interactivity to services.swpc.noaa.gov/images/animations. uses prefect for a backend to scrape images generate movies with ffmpeg. flask frontend at /iframe + proxy server via bare domain and API access via /api.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

153 lines
5.1 KiB

import logging
import os
import re
import time
from datetime import datetime
import requests
from flask import Flask, Response, render_template, request, send_from_directory
from prefect.deployments import run_deployment
PORT = 9021
PROTO = "https"
BARE = True
app = Flask(__name__)
logging.basicConfig(level=logging.DEBUG)
def deploy_name():
return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S") + "Z"
def get_host():
host = os.environ.get("LIGHTNING_CLOUDSPACE_HOST")
if host is None:
default_host = os.environ.get("HOST_NAME", "0.0.0.0")
return f"{default_host}:{PORT}" if not BARE else f"{default_host}"
else:
return f"{PORT}-{host}" if not BARE else f"{host}"
@app.route("/iframe")
@app.route("/iframe/")
@app.route("/iframe/<path:subpath>")
def home(subpath="images/animations/", proto=PROTO):
host = get_host()
initial_url = f"{proto}://{host}/{subpath}"
api_url = f"{proto}://{host}/api"
return render_template(
"index.html", initial_url=initial_url, host=f"{proto}://{host}", api_url=api_url
)
@app.route("/api", methods=["POST"])
def handle_api():
data = request.json # This assumes you're sending JSON data.
url = data.get("url")
if not url.endswith("/"):
url += "/"
logging.debug(f"Received URL: {url}")
params = {"url": url, "limit": 24 * 60, "ext": None}
response = run_deployment(
name="create-animations/noaa-animate",
parameters=params,
flow_run_name=f"{deploy_name()}.webapp.{url}",
)
# response is a FlowRun - need to get what we want from it.
# Process the data as needed.
return {
"status": "success",
"message": f"{url} processed successfully",
# "response": response,
}, 200
@app.route("/videos/<path:filename>")
def custom_static(filename):
return send_from_directory("../out", filename)
@app.route("/", methods=["GET"])
@app.route("/<path:url>", methods=["GET"])
def proxy(url="", proto=PROTO):
original_base_url = "https://services.swpc.noaa.gov"
host = get_host()
proxy_base_url = f"{proto}://{host}/"
target_url = f"{original_base_url}/{url}"
logging.debug(f"Fetching URL: {target_url}")
try:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
}
response = requests.get(target_url, headers=headers, stream=True)
excluded_headers = [
"content-encoding",
"content-length",
"transfer-encoding",
"connection",
]
headers = [
(name, value)
for (name, value) in response.raw.headers.items()
if name.lower() not in excluded_headers
]
if "text/html" in response.headers.get("Content-Type", ""):
content = response.content.decode("utf-8")
content = re.sub(r"'http://", "'https://", content)
content = re.sub(
r"https?://services.swpc.noaa.gov", proxy_base_url, content
)
content = content.replace(
"</body>",
f"""
<script>
window.addEventListener('load', function() {{
var hasSufficientImages = false;
var observer = new MutationObserver(function(mutations) {{
mutations.forEach(function(mutation) {{
if (mutation.type === 'childList') {{
console.log('observer');
checkImages();
}}
}});
}});
observer.observe(document.body, {{ childList: true, subtree: true }});
function checkImages() {{
var links = document.querySelectorAll('a');
var imageLinkRegex = /\.(jpg|jpeg|png)(\\?.*)?(#.*)?$/i;
var numImages = Array.from(links).filter(link => imageLinkRegex.test(link.href)).length;
console.log('Number of eligible links:', numImages);
hasSufficientImages = numImages >= 60;
window.parent.postMessage({{ type: 'urlUpdate', url: '{original_base_url}/{url}', eligible: hasSufficientImages }}, '*');
}}
// Run initial check in case content is already loaded
console.log('initial');
checkImages();
}});
</script>
</body>""",
)
content = content.encode("utf-8")
return Response(content, status=response.status_code, headers=headers)
else:
return Response(
response.content, status=response.status_code, headers=headers
)
except Exception as e:
logging.error(f"Error fetching URL: {e}")
return Response(f"Error fetching URL: {e}", status=500)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=9021, debug=True)