You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask渲染模板无法自动更新Spotify当前播放曲目问题

问题根源

你当前的实现逻辑完全错误:

  1. HTTP是请求-响应模型,后端无法主动向已加载页面的浏览器推送新内容。线程里的return render_template只是生成了HTML字符串,但没有任何通道把它发送到客户端。
  2. 全局变量trackg线程不安全,多用户访问时会互相覆盖数据,完全不可用。
  3. 未处理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 06:10:31