树莓派Ubuntu环境下Spotipy无显示器用户认证方案咨询
解决树莓派无显示器环境下Spotify语音助手的认证难题
核心问题
当前使用SpotifyOAuth默认依赖本地浏览器完成授权,但无显示器的树莓派无法操作;语音输入密码存在严重安全隐患。需要安全、简便地在无显示器环境完成Spotify用户授权。
方案一:远程授权(将验证链接发送至其他设备)
通过手动获取授权URL,让用户在其他设备完成登录后,返回授权码完成认证。
实现步骤
关闭自动浏览器跳转,手动获取授权URL
修改认证逻辑,禁用自动打开浏览器,通过语音播报授权链接:from spotipy.oauth2 import SpotifyOAuth # 初始化OAuth对象,禁止自动打开浏览器 sp_oauth = SpotifyOAuth( client_id="aaa000", client_secret="bbb111", redirect_uri="http://127.0.0.1:9090", scope="user-library-read,user-read-playback-state,user-modify-playback-state", open_browser=False ) # 获取授权URL并语音播报 auth_url = sp_oauth.get_authorize_url() engine.say(f"请在其他设备打开链接完成授权:{auth_url}") engine.runAndWait()获取授权码并完成认证
用户在其他设备登录后,Spotify会跳转到设置的redirect_uri,URL中包含code参数。让用户通过语音告知该参数,程序即可完成认证:engine.say("授权完成后,请告诉我回调URL里的code参数") engine.runAndWait() auth_code = listen().strip() # 用code获取token并初始化Spotify客户端 token_info = sp_oauth.get_access_token(auth_code) sp = spotipy.Spotify(auth=token_info['access_token'])本地保存token,避免重复授权
将token写入本地文件,下次启动直接读取,过期自动刷新:import json import os TOKEN_FILE = "spotify_token.json" if os.path.exists(TOKEN_FILE): with open(TOKEN_FILE, 'r') as f: token_info = json.load(f) if sp_oauth.is_token_expired(token_info): token_info = sp_oauth.refresh_access_token(token_info['refresh_token']) with open(TOKEN_FILE, 'w') as f: json.dump(token_info, f) sp = spotipy.Spotify(auth=token_info['access_token']) else: # 执行上述授权流程后,保存token到文件 with open(TOKEN_FILE, 'w') as f: json.dump(token_info, f)
方案二:设备授权流(官方推荐无浏览器方案)
Spotify提供专门针对无浏览器设备的设备授权流,无需用户输入复杂授权码,流程更简便。
实现代码
from spotipy.oauth2 import SpotifyDeviceAuth # 初始化设备授权对象 sp_oauth = SpotifyDeviceAuth( client_id="aaa000", client_secret="bbb111", scope="user-library-read,user-read-playback-state,user-modify-playback-state", open_browser=False ) # 获取验证信息并语音播报 auth_info = sp_oauth.get_device_authorization() engine.say(f"请打开网址 {auth_info['verification_uri']},输入验证码 {auth_info['user_code']},完成后告诉我") engine.runAndWait() # 等待用户确认授权完成 while True: response = listen() if "完成" in response or "好了" in response: break engine.say("请确认授权完成后告诉我") # 获取token并初始化客户端 token_info = sp_oauth.get_access_token() sp = spotipy.Spotify(auth=token_info['access_token'])
流程说明
程序启动后会生成一个验证网址和8位验证码,用户在任意设备浏览器打开网址、输入验证码并登录Spotify账号,即可完成授权。验证码有效期为15分钟,超时需重新生成。
整合后的完整代码(设备授权流+token持久化)
import spotipy from spotipy.oauth2 import SpotifyDeviceAuth import pyttsx3 import speech_recognition as sr import json import os engine = pyttsx3.init() recognizer = sr.Recognizer() TOKEN_FILE = "spotify_token.json" def listen(): with sr.Microphone() as source: recognizer.adjust_for_ambient_noise(source) audio = recognizer.listen(source) try: return recognizer.recognize_google(audio).strip() except sr.UnknownValueError: engine.say("没听清,请再说一遍") engine.runAndWait() return listen() except sr.RequestError: engine.say("网络异常,无法识别语音") engine.runAndWait() return listen() def init_spotify(): sp_oauth = SpotifyDeviceAuth( client_id="aaa000", client_secret="bbb111", scope="user-library-read,user-read-playback-state,user-modify-playback-state", open_browser=False ) # 读取本地token if os.path.exists(TOKEN_FILE): with open(TOKEN_FILE, 'r') as f: token_info = json.load(f) if sp_oauth.is_token_expired(token_info): token_info = sp_oauth.refresh_access_token(token_info['refresh_token']) with open(TOKEN_FILE, 'w') as f: json.dump(token_info, f) return spotipy.Spotify(auth=token_info['access_token']) else: # 首次授权流程 auth_info = sp_oauth.get_device_authorization() engine.say(f"请打开网址 {auth_info['verification_uri']},输入验证码 {auth_info['user_code']},完成后告诉我") engine.runAndWait() while True: response = listen() if "完成" in response or "好了" in response: break engine.say("请确认授权完成后告诉我") engine.runAndWait() token_info = sp_oauth.get_access_token() with open(TOKEN_FILE, 'w') as f: json.dump(token_info, f) return spotipy.Spotify(auth=token_info['access_token']) def play_song(song_name, sp): results = sp.search(q=song_name, limit=1) if not results['tracks']['items']: engine.say(f"未找到歌曲 {song_name}") engine.runAndWait() return song_id = results['tracks']['items'][0]['id'] try: sp.start_playback(uris=[f"spotify:track:{song_id}"]) engine.say(f"正在播放 {song_name}") engine.runAndWait() except spotipy.exceptions.SpotifyException: engine.say("请确保Spotify设备已登录你的账号并处于运行状态") engine.runAndWait() if __name__ == "__main__": engine.say("正在初始化Spotify,请稍等") engine.runAndWait() sp = init_spotify() engine.say("你想播放什么歌?") engine.runAndWait() song_name = listen() if song_name: play_song(song_name, sp)
注意事项
- 确保Spotify开发者应用中已添加方案一所需的
redirect_uri,设备授权流默认启用无需额外配置。 - 保存token的文件需设置合理权限,避免被其他用户读取。
- 设备授权流的验证码有效期为15分钟,需提醒用户及时完成授权。
内容的提问来源于stack exchange,提问作者Arturo Medina
相关产品推荐
相关产品推荐

