# app.py # Copyright (c) 2025 snfsx.xyz # Licensed under the MIT License. See https://opensource.org/licenses/MIT import os import datetime import requests from flask import Flask, render_template, jsonify, request, send_from_directory from dotenv import load_dotenv from werkzeug.middleware.proxy_fix import ProxyFix from flask_limiter import Limiter from flask_limiter.util import get_remote_address # Load environment variables from .env file load_dotenv() app = Flask(__name__) app.wsgi_app = ProxyFix( app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1 ) limiter = Limiter( get_remote_address, app=app, default_limits=["300 per day", "50 per hour"], storage_uri="memory://", ) # --- Configuration from .env --- lastfm_apik = os.getenv('LASTFM_API_KEY') lastfm_usrname = os.getenv('LASTFM_USERNAME') usrname = os.getenv('USER_NAME', 'User') age = os.getenv('USER_AGE', '') bio = os.getenv('USER_BIO', '#') tg = os.getenv('USER_TELEGRAM_TAG') bd = os.getenv('USER_BIRTHDAY', '') github_usrn = os.getenv('GITHUB_USERNAME') email = os.getenv('EMAIL_ADDRESS') jabber = os.getenv('JABBER_ADDRESS') ton = os.getenv('TON_ADDRESS') ton_v = os.getenv('TON_VERSION') favicon = os.getenv('FAVICON_TEXT') # --------------------------------- # ----------- Functions -------------- def get_lastfm_now_playing(): """Fetches the currently playing track from Last.fm API.""" if not lastfm_apik or not lastfm_usrname: return {'error': 'Last.fm API key or username not configured.'} api_url = f"http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user={lastfm_usrname}&api_key={lastfm_apik}&format=json&limit=1" try: response = requests.get(api_url, timeout=10) # Added timeout response.raise_for_status() # Raise an exception for bad status codes data = response.json() if 'recenttracks' in data and 'track' in data['recenttracks'] and data['recenttracks']['track']: track_data = data['recenttracks']['track'][0] is_now_playing = track_data.get('@attr', {}).get('nowplaying') == 'true' if is_now_playing: artist = track_data['artist']['#text'] name = track_data['name'] # Get the largest available image URL image_url = next((img['#text'] for img in reversed(track_data.get('image', [])) if img['#text']), None) # Construct a plausible song.link URL (accuracy may vary) song_link = f"https://song.link/s/{requests.utils.quote(f'{name} {artist}')}" return { 'playing': True, 'artist': artist, 'name': name, 'image_url': image_url, 'song_link': song_link } else: return {'playing': False, 'message': 'Not listening anything right now.'} else: # Handle cases where user has no tracks or invalid response structure return {'playing': False, 'message': 'No recent tracks found.'} except requests.exceptions.RequestException as e: print(f"Error fetching Last.fm data: {e}") return {'error': f'Could not connect to Last.fm API: {e}'} except Exception as e: print(f"An unexpected error occurred: {e}") return {'error': 'An unexpected error occurred while fetching Last.fm data.'} # ------- end functions ------------ # ----------- Routes ---------------- @app.route('/') def index(): """Renders the main page.""" personal_info = { 'name': usrname, 'age': age, 'bio': bio, 'telegram': tg, 'birthday': bd, 'github_username': github_usrn, 'email': email, 'jabber': jabber, 'ton_version': ton_v, 'ton_address': ton, 'favicon': favicon, 'nickname': usrname.lower(), 'year': datetime.datetime.now().year } host = request.host.split(':')[0] ip = request.headers.get('X-Forwarded-For', request.remote_addr) if host == "snfsx.xyz" or host == "tests.snfsx.xyz": return render_template('index.html', info=personal_info) elif host == "ip.snfsx.xyz": return jsonify({"ip": ip}) else: return jsonify({"error": "unknown host"}) @app.route('/api/v1/ip') def ip_return(): ip = request.headers.get('X-Forwarded-For', request.remote_addr) return jsonify({"ip": ip}) @app.route('/gamelife') def gamelife_page(): """Just gamelife """ return send_from_directory('static/html', 'gamelife.html') @app.route('/api/v1/now_playing') @limiter.limit("1 per 5 second") def api_now_playing(): """API endpoint to get current Last.fm track.""" track_info = get_lastfm_now_playing() return jsonify(track_info) # -------------- End routes -------------- # ------------ Error handlers ----------- @app.errorhandler(429) def ratelimit_error(e): """ 429 handler """ return jsonify( error="ratelimit_exceeded", message="Too many requests", ), 429 @app.errorhandler(404) def handle_not_found(error): """404 handler""" app.logger.warning(f"404 Not Found: {request.path}") return render_template('404.html'), 404 # ------------ End error handlers ------------- # Start if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False)