Flask渲染模板无法自动更新Spotify当前播放曲目问题
问题根源
你当前的实现逻辑完全错误:
- HTTP是请求-响应模型,后端无法主动向已加载页面的浏览器推送新内容。线程里的
return render_template只是生成了HTML字符串,但没有任何通道把它发送到客户端。 - 全局变量
trackg线程不安全,多用户访问时会互相覆盖数据,完全不可用。 - 未处理
current_user_playing_track()返回None的情况(比如用户暂停播放),会导致线程报错崩溃。
解决方案:前端定时请求API(简单易实现)
这是最适合新手的方案,通过前端定时向后端请求最新播放信息,然后更新页面DOM。
步骤1:重构后端,新增API接口
from flask import jsonify @app.route('/current-track') def current_track(): cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session) auth_manager = spotipy.oauth2.SpotifyOAuth(cache_handler=cache_handler, show_dialog=False, scope='user-read-currently-playing') # 验证用户授权 if not auth_manager.validate_token(cache_handler.get_cached_token()): return jsonify({'error': 'unauthorized'}), 401 spotify = spotipy.Spotify(auth_manager=auth_manager) try: current = spotify.current_user_playing_track() # 处理播放器闲置状态 if not current or not current.get('item'): return jsonify({ 'track': 'Your Player Is Idle', 'artist': 'Visuafy Will Refresh Automatically', 'image': url_for('static', filename='idle.jpeg', _external=True) }) # 提取曲目信息 track = current['item']['name'] artist = current['item']['artists'][0]['name'] image = current['item']['album']['images'][0]['url'] return jsonify({'track': track, 'artist': artist, 'image': image}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/vintage') def vintage(): cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session) auth_manager = spotipy.oauth2.SpotifyOAuth(cache_handler=cache_handler, show_dialog=False, scope='user-read-currently-playing') if not auth_manager.validate_token(cache_handler.get_cached_token()): return redirect('/') # 获取初始数据,避免页面加载时空白 initial_response = current_track() initial_data = initial_response.get_json() return render_template('vintage.html', initial_data=initial_data)
步骤2:修改HTML,添加前端定时更新逻辑
<html> <body> <div class="d-flex flex-column justify-content-center w-100 h-100"> <div class="d-flex flex-column justify-content-center align-items-center"> <div class="center"> <center> <img id="track-image" src="{{ initial_data.image }}" height="300" width="300" style="border-radius: 5%;"> <h1 id="track-name">{{ initial_data.track }}</h1> <h3 id="artist-name">{{ initial_data.artist }}</h3> </center> </div> </div> </div> <script> // 刷新间隔(毫秒),建议5-10秒,避免频繁请求被Spotify限流 const REFRESH_INTERVAL = 5000; function updateTrackInfo() { fetch('/current-track') .then(res => res.json()) .then(data => { if (data.error) { // 未授权时跳转到登录页 if (data.error === 'unauthorized') { window.location.href = '/'; } return; } // 更新页面元素 document.getElementById('track-image').src = data.image; document.getElementById('track-name').textContent = data.track; document.getElementById('artist-name').textContent = data.artist; }) .catch(err => console.error('更新失败:', err)); } // 页面加载后立即更新一次,然后启动定时任务 updateTrackInfo(); setInterval(updateTrackInfo, REFRESH_INTERVAL); </script> </body> </html>
进阶方案:WebSocket实时推送(低延迟)
如果需要更实时的更新(比如曲目切换立即刷新),可以用WebSocket实现后端主动推送。需要安装flask-socketio和eventlet:
pip install flask-socketio eventlet
后端代码修改
from flask_socketio import SocketIO, emit app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key-here' socketio = SocketIO(app, cors_allowed_origins="*") @app.route('/vintage') def vintage(): cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session) auth_manager = spotipy.oauth2.SpotifyOAuth(cache_handler=cache_handler, show_dialog=False, scope='user-read-currently-playing') if not auth_manager.validate_token(cache_handler.get_cached_token()): return redirect('/') # 获取初始数据 initial_response = current_track() initial_data = initial_response.get_json() return render_template('vintage.html', initial_data=initial_data) def track_monitor(auth_manager): spotify = spotipy.Spotify(auth_manager=auth_manager) last_track_id = None while True: try: current = spotify.current_user_playing_track() current_id = current['item']['id'] if current and current.get('item') else None if current_id != last_track_id: last_track_id = current_id if current and current.get('item'): data = { 'track': current['item']['name'], 'artist': current['item']['artists'][0]['name'], 'image': current['item']['album']['images'][0]['url'] } else: data = { 'track': 'Your Player Is Idle', 'artist': 'Visuafy Will Refresh Automatically', 'image': url_for('static', filename='idle.jpeg', _external=True) } # 推送更新到当前客户端 emit('track_update', data) # 每2秒检查一次,避免频繁请求 socketio.sleep(2) except Exception as e: print(f"监控出错: {e}") break @socketio.on('connect') def handle_connect(): cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session) auth_manager = spotipy.oauth2.SpotifyOAuth(cache_handler=cache_handler, show_dialog=False, scope='user-read-currently-playing') if auth_manager.validate_token(cache_handler.get_cached_token()): # 启动后台监控线程 socketio.start_background_task(track_monitor, auth_manager)
HTML修改(添加WebSocket逻辑)
<html> <body> <div class="d-flex flex-column justify-content-center w-100 h-100"> <div class="d-flex flex-column justify-content-center align-items-center"> <div class="center"> <center> <img id="track-image" src="{{ initial_data.image }}" height="300" width="300" style="border-radius: 5%;"> <h1 id="track-name">{{ initial_data.track }}</h1> <h3 id="artist-name">{{ initial_data.artist }}</h3> </center> </div> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script> <script> const socket = io(); // 接收后端推送的更新 socket.on('track_update', function(data) { document.getElementById('track-image').src = data.image; document.getElementById('track-name').textContent = data.track; document.getElementById('artist-name').textContent = data.artist; }); </script> </body> </html>
关键注意事项
- Spotify API有请求限流,不要设置过短的刷新间隔(建议最低2秒)。
- 永远不要用全局变量存储用户相关数据,会导致多用户冲突。
- 处理好授权过期的情况,前端收到
unauthorized错误时要跳转到登录页。
内容的提问来源于stack exchange,提问作者Aryan Bhajanka
相关产品推荐
相关产品推荐

