首页 > 编程问答 >Spotipy 未正确存储用户信息

Spotipy 未正确存储用户信息

时间:2024-07-25 04:17:33浏览次数:11  
标签:python flask spotify spotipy flask-session

我在使用 Flask 会话和 Spotify OAuth access_token 时遇到问题(我正在使用 Spotipy)。它没有正确存储用户的信息

我几个月前开始编程,我正在使用 Python 开始我的第一个“大”项目,但当用户登录我的网络应用程序时,我在处理信息时遇到了麻烦。即使当不同的用户登录时,它也始终显示我的 Spotify 用户名和播放列表信息。例如,如果一个名为“James”的用户登录,当他在 auth_url 中时,它会显示他的个人资料,但当它实际访问我的网页“getPlaylists”时,显示的所有信息都是我的。 Google/YouTube Oauth 部分工作正常,但 Spotify Oauth 则不然。

我在 Spotify 的开发者仪表板中添加了测试用户,但没有解决问题。关于应该做什么有什么建议吗?我没有正确处理会话吗? 我不知道是否应该将所有代码粘贴到此处,但大部分代码如下:

import spotipy
from spotipy.oauth2 import SpotifyOAuth

from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow, Flow
from google.auth.transport.requests import Request
import google.oauth2.credentials

import json
import os
import os.path

from flask import Flask, request, url_for, session, redirect, render_template
from flask_session import Session

import clientinfo # File containing all the info necessary to login the web APP to the Spotify and youtube APIs


app = Flask(__name__)
if __name__ == "__main__":
    app.run(debug=True)

app.secret_key = os.environ['FLASK_S_KEY']

app.config['SESSION_COOKIE_NAME'] = 'Spotify Cookie'

# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)

TOKEN_INFO = "token_info"

@app.after_request
def after_request(response):
    """Ensure responses aren't cached"""
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Expires"] = 0
    response.headers["Pragma"] = "no-cache"
    return response


def create_spotify_oauth():
    return SpotifyOAuth(
        client_id = os.environ['SPOTIFY_CLIENT_ID'],
        client_secret = os.environ['SPOTIFY_CLIENT_SECRET'],
        redirect_uri = url_for('redirectSite', _external=True),
        scope = "user-read-private playlist-read-private playlist-read-collaborative",
        show_dialog=True
    )

def get_token():
    token_info = session.get(TOKEN_INFO, None)
    if not token_info:
        raise Exception("Token not found in session")
    
    sp_oauth = create_spotify_oauth()

    if sp_oauth.is_token_expired(token_info):
        token_info = sp_oauth.refresh_access_token(token_info['refresh_token'])
        session[TOKEN_INFO] = token_info
    
    print(f"Token info: {token_info}") # Debug
    return token_info

def credentialscheck():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
    
    json_credentials = session.get('credentials')
    
    if 'credentials' in session:
        dict_credentials = json.loads(json_credentials)
        credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(dict_credentials)
        

        if credentials.expired:
            credentials.refresh(Request())

    else:
        return redirect(url_for('redirectYT'))

# Endpoint Index
@app.route('/', methods=["GET"])
def index():
    if request.method == "GET":
        return render_template("index.html")
    

# Log in user to the web app using Spotify OAuth
@app.route("/login")
def login():
     # Forget any user_id
    session.clear()

    sp_oauth = create_spotify_oauth()
    auth_url = sp_oauth.get_authorize_url()
    return redirect(auth_url)

@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("index", _external=True))

# After login, redirect user to the page with the correct authorization token
@app.route("/redirect")
def redirectSite():
    sp_oauth = create_spotify_oauth()
    code = request.args.get("code")
    token_info = sp_oauth.get_access_token(code)
    session[TOKEN_INFO] = token_info

    return redirect(url_for("redirectYT", _external=True))

@app.route("/redirectYT")
def redirectYT():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

   # Create OAuth flow object
    flow = Flow.from_client_secrets_file(
        'client_secrets.json',
        scopes=["https://www.googleapis.com/auth/youtube.force-ssl"])
    flow.redirect_uri = url_for('callback', _external=True)
    authorization_url, state = flow.authorization_url(
        access_type='offline',
        prompt='select_account')
    
    # Save the state so we can verify the request later
    session['state'] = state
    
    return redirect(authorization_url)

@app.route('/callback')
def callback():
    # Verify the request state
    if request.args.get('state') != session['state']:
        raise Exception('Invalid state')
    
    # Create the OAUth flow object
    flow = InstalledAppFlow.from_client_secrets_file(
        'client_secrets.json',
        scopes=["https://www.googleapis.com/auth/youtube.force-ssl"],
        state=session['state'])
    flow.redirect_uri = url_for('callback', _external=True)

    # Exchange the authorization code for an access token
    authorization_response = request.url
    flow.fetch_token(authorization_response=authorization_response)

    # Save credentials to the session
    credentials = flow.credentials
    session['credentials'] = credentials.to_json()

    return redirect(url_for('getPlaylists'))

@app.route('/getPlaylists')
def getPlaylists():
    try: 
       token_info = get_token()
       
    except Exception as e:
       print(f"Exception: {e}")
       return redirect(url_for("login", _external=True))
    
    sp = spotipy.Spotify(auth=token_info['access_token'])
    credentialscheck()
    user = sp.current_user()
    username = user['display_name']
    print(f"User: {username}") # Debug
    
    # Getting all playlists (since the current_user_playlists max limit is 50, we need a 'for' loop)
    allPlaylists = []
    i = 0
    while True:
        fiftyplaylists = sp.current_user_playlists(limit=50, offset=i * 50)['items']
        i += 1
        allPlaylists += fiftyplaylists
        if (len(fiftyplaylists)< 50):
            break

    # Filtering the data we actually need in each playlist and sorting them alphabetically)
    playlists = [{'name': playlist['name'], 'id': playlist['id']} for playlist in allPlaylists]
    playlists.sort(key=lambda x: x['name'])

    return render_template("getPlaylists.html", playlists=playlists, username=username)

我已经尝试打印 token_info 并且在 getPlaylist 中它看起来总是相同的,用户名也总是相同的。恐怕我没有正确存储用户的 token_info ,但我看不到其他方法来修复它。我尝试了在 Stack Overflow 上看到的其他代码选项,但使用它时发生的情况是一样的。

import spotipy
from spotipy.oauth2 import SpotifyOAuth

from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow, Flow
from google.auth.transport.requests import Request
import google.oauth2.credentials

import json
import os
import os.path
import time

from flask import Flask, request, url_for, session, redirect, render_template
from flask_session import Session

import clientinfo # File containing all the info necessary to login the web APP to the Spotify and youtube APIs

""" requirements [
    Flask,
    Spotipy,
    OS,
    time,
    clientinfo
]"""

app = Flask(__name__)
if __name__ == "__main__":
    app.run(debug=True)

app.secret_key = os.environ['FLASK_S_KEY']

app.config['SESSION_COOKIE_NAME'] = 'Spotify Cookie'

# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)


@app.after_request
def after_request(response):
    """Ensure responses aren't cached"""
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Expires"] = 0
    response.headers["Pragma"] = "no-cache"
    return response


def create_spotify_oauth():
    return SpotifyOAuth(
        client_id = os.environ['SPOTIFY_CLIENT_ID'],
        client_secret = os.environ['SPOTIFY_CLIENT_SECRET'],
        redirect_uri = url_for('redirectSite', _external=True),
        scope = "user-read-private playlist-read-private playlist-read-collaborative",
        show_dialog=True
    )

def credentialscheck():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
    
    json_credentials = session.get('credentials')
    
    if 'credentials' in session:
        dict_credentials = json.loads(json_credentials)
        credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(dict_credentials)
        

        if credentials.expired:
            credentials.refresh(Request())

    else:
        return redirect(url_for('redirectYT'))

# Endpoint Index
@app.route('/', methods=["GET"])
def index():
    if request.method == "GET":
        return render_template("index.html")
    

# Log in user to the web app using Spotify OAuth
@app.route("/login")
def login():
     # Forget any user_id
    session.clear()

    sp_oauth = create_spotify_oauth()
    auth_url = sp_oauth.get_authorize_url()
    return redirect(auth_url)

@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("index", _external=True))

# After login, redirect user to the page with the correct authorization token
@app.route("/redirect")
def redirectSite():
    sp_oauth = create_spotify_oauth()
    session.clear()
    code = request.args.get("code")
    token_info = sp_oauth.get_access_token(code)

    session["token_info"] = token_info

    return redirect(url_for("redirectYT", _external=True))

@app.route("/redirectYT")
def redirectYT():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

   # Create OAuth flow object
    flow = Flow.from_client_secrets_file(
        'client_secrets.json',
        scopes=["https://www.googleapis.com/auth/youtube.force-ssl"])
    flow.redirect_uri = url_for('callback', _external=True)
    authorization_url, state = flow.authorization_url(
        access_type='offline',
        prompt='select_account')
    
    # Save the state so we can verify the request later
    session['state'] = state
    
    return redirect(authorization_url)

@app.route('/callback')
def callback():
    # Verify the request state
    if request.args.get('state') != session['state']:
        raise Exception('Invalid state')
    
    # Create the OAUth flow object
    flow = InstalledAppFlow.from_client_secrets_file(
        'client_secrets.json',
        scopes=["https://www.googleapis.com/auth/youtube.force-ssl"],
        state=session['state'])
    flow.redirect_uri = url_for('callback', _external=True)

    # Exchange the authorization code for an access token
    authorization_response = request.url
    flow.fetch_token(authorization_response=authorization_response)

    # Save credentials to the session
    credentials = flow.credentials
    session['credentials'] = credentials.to_json()

    return redirect(url_for('getPlaylists'))

@app.route('/getPlaylists')
def getPlaylists():
    session['token_info'], authorized = get_token(session)
    session.modified = True
    if not authorized:
        print("User not authorized!")
        return redirect('/')
    credentialscheck()

    sp = spotipy.Spotify(auth = session.get('token_info').get('access_token'))
    user = sp.current_user()
    username = user['display_name']
    print(f"User: {username}") # Debug
    
    # Getting all playlists (since the current_user_playlists max limit is 50, we need a 'for' loop)
    allPlaylists = []
    i = 0
    while True:
        fiftyplaylists = sp.current_user_playlists(limit=50, offset=i * 50)['items']
        i += 1
        allPlaylists += fiftyplaylists
        if (len(fiftyplaylists)< 50):
            break

    # Filtering the data we actually need in each playlist and sorting them alphabetically)
    playlists = [{'name': playlist['name'], 'id': playlist['id']} for playlist in allPlaylists]
    playlists.sort(key=lambda x: x['name'])

    return render_template("getPlaylists.html", playlists=playlists, username=username)

def get_token(session):
    token_valid = False
    token_info = session.get("token_info", None)

    if not (session.get('token_info', False)):
        token_valid = False
        return token_info, token_valid
    
    now = int(time.time())
    is_token_expired = session.get('token_info').get('expires_at') - now < 60

    if (is_token_expired):
        sp_oauth = create_spotify_oauth()
        token_info = sp_oauth.refresh_access_token(session.get('token_info').get('refresh_token'))
    
    token_valid = True
    return token_info, token_valid

问题出在的 Spotify OAuth 对象创建方式上。正在为每个请求/会话创建一个新的 SpotifyOAuth 对象,这会导致它不保留任何先前的登录信息,并始终使用自己的凭据默认登录。

要解决此问题,需要在全局范围内创建一个 SpotifyOAuth 实例,然后在整个应用程序中使用该实例。

以下是更新后的代码:

import spotipy
from spotipy.oauth2 import SpotifyOAuth
# ... other imports

app = Flask(__name__)
# ... other configurations

# 创建全局 SpotifyOAuth 实例
sp_oauth = SpotifyOAuth(
    client_id=os.environ['SPOTIFY_CLIENT_ID'],
    client_secret=os.environ['SPOTIFY_CLIENT_SECRET'],
    redirect_uri=url_for('redirectSite', _external=True),
    scope="user-read-private playlist-read-private playlist-read-collaborative",
    show_dialog=True
)

# ... other routes

@app.route("/redirect")
def redirectSite():
    # 使用全局 sp_oauth 实例
    code = request.args.get("code")
    token_info = sp_oauth.get_access_token(code)
    session[TOKEN_INFO] = token_info
    return redirect(url_for("redirectYT", _external=True))

# ... other routes

@app.route('/getPlaylists')
def getPlaylists():
    try: 
        token_info = get_token()
    except Exception as e:
        print(f"Exception: {e}")
        return redirect(url_for("login", _external=True))

    # 使用全局 sp_oauth 实例获取 Spotify 对象
    sp = spotipy.Spotify(auth=token_info['access_token'])
    # ... rest of the code

通过这些更改,的 SpotifyOAuth 实例将在应用程序的整个生命周期中保持活动状态,并正确保留用户登录信息。 这将解决遇到的问题,并允许不同的用户使用他们自己的 Spotify 帐户登录。

标签:python,flask,spotify,spotipy,flask-session
From: 78776796

相关文章

  • a method to make some handy tools with python
    Inmyworkingofcomputer,therearealotofsimplejobsthatarefrequentlyrepeated.Itriedtofindawaytomakethesejobbeenprocessedeasily.Method1:Themethodiswritingascripttodothejob,andexecutingthescriptbyutoolsextensionuto......
  • Python网络爬虫详解:实战豆瓣电影信息采集
    文章目录前言一、爬虫是什么?二、常用库及其作用1.Requests2.BeautifulSoup3.lxml4.Scrapy5.Selenium6.PyQuery7.Pandas8.JSON9.Time三、实现步骤步骤一:环境准备步骤二:数据采集步骤三:数据处理步骤四:数据存储总结前言随着互联网的迅猛发展和数据分析需求的不......
  • python学习之内置函数
    Python拥有许多内置函数,这些函数是Python的一部分,不需要额外导入即可直接使用。这些函数提供了对Python解释器功能的直接访问,涵盖了从数学计算到类型检查、从内存管理到异常处理等各个方面。下面是一些常用的Python内置函数及其简要说明:一、Printprint函数大家都不会......
  • Python中以函数为作用域
    点击查看代码#第一题foriteminrange(10):#不报错,没有函数,所有操作在全局作用域里面执行,item最后赋值为:9,此时item在缩进与全局都可以使用passprint(item)#第二题item=10deffunc():foriteminrange(10):#优先在本地查找,找不到在到全局查找p......
  • 掌握IPython宏:%%macro命令的高效使用指南
    掌握IPython宏:%%macro命令的高效使用指南在编程中,宏是一种允许你定义可重用代码片段的强大工具。IPython,这个增强版的Python交互式环境,提供了一个名为%%macro的魔术命令,允许用户创建宏,从而提高代码的可重用性和效率。本文将详细介绍如何在IPython中使用%%macro命令创建宏,并......
  • 7月24号python:库存管理
    7月24号python:库存管理题目:​ 仓库管理员以数组stock形式记录商品库存表。stock[i]表示商品id,可能存在重复。原库存表按商品id升序排列。现因突发情况需要进行商品紧急调拨,管理员将这批商品id提前依次整理至库存表最后。请你找到并返回库存表中编号的最小的元素以便及......
  • IPython的Bash之舞:%%bash命令全解析
    IPython的Bash之舞:%%bash命令全解析IPython的%%bash魔术命令为JupyterNotebook用户提供了一种在单元格中直接执行Bash脚本的能力。这个特性特别适用于需要在Notebook中运行系统命令或Bash特定功能的场景。本文将详细介绍如何在IPython中使用%%bash命令,并提供实际的代码示......
  • Python数据分析与可视化大作业项目说明(含免费代码)
    题目:对全球和中国互联网用户的数据分析与可视化代码下载链接:https://download.csdn.net/download/s44359487yad/89574688一、项目概述1.1.项目背景:互联网是当今时代最重要和最有影响力的技术之一,它已经深刻地改变了人们的生活、工作、学习等方面。互联网用户数据是反映......
  • IPython的跨界魔术:%%javascript命令深度解析
    IPython的跨界魔术:%%javascript命令深度解析IPython,作为Python编程的强大交互式工具,提供了多种魔术命令来扩展其功能。其中,%%javascript魔术命令允许用户在IPythonNotebook中直接执行JavaScript代码,打通了Python和JavaScript两个世界,为数据可视化、Web内容操作等提供了便......
  • 密码学-RSA基础题解题脚本-Python
    importgmpy2#计算大整数模块importlibnumimportrsafromCrypto.PublicKeyimportRSA#安装时安装pycryptodome模块#已知:p,q,e,cdefknown_p_q_e_c():p=int(input('请输入一个素数p:'))q=int(input('请输入另一个素数q:'))e=int(input('请输入公钥e:'))......