我正在创建一个使用 Spotify Web API 和 OpenAI API 的 node.js Web 应用程序。服务器端代码基本上接受用户的提示,使用 OpenAI 语言模型对其进行处理,然后从 Spotify 获取曲目并构建播放列表。该应用程序成功获取提示、处理并获取曲目(我可以在我的控制台中看到正在获取的曲目),但之后我收到 TypeError: callback is not a function。似乎错误是由 Spotify Web API 中的 http-manager.js 文件引发的。
我认为我什至没有在我的代码中引用回调,这可能是 Spotify 的 API 问题/我尝试与之交互的方式吗?
这是测试应用程序时我的控制台中的确切错误:
TypeError: callback is not a function
at /rbd/pnpm-volume/bfe9bf90-d68c-4e41-8b2a-95cefb846cfe/node_modules/spotify-web-api-node/src/http-manager.js:71:16
at Request.callback (/rbd/pnpm-volume/bfe9bf90-d68c-4e41-8b2a-95cefb846cfe/node_modules/spotify-web-api-node/node_modules/superagent/lib/node/index.js:905:3)
at /rbd/pnpm-volume/bfe9bf90-d68c-4e41-8b2a-95cefb846cfe/node_modules/spotify-web-api-node/node_modules/superagent/lib/node/index.js:1127:20
at IncomingMessage.<anonymous> (/rbd/pnpm-volume/bfe9bf90-d68c-4e41-8b2a-95cefb846cfe/node_modules/spotify-web-api-node/node_modules/superagent/lib/node/parsers/json.js:22:7)
at Stream.emit (events.js:400:28)
at Unzip.<anonymous> (/rbd/pnpm-volume/bfe9bf90-d68c-4e41-8b2a-95cefb846cfe/node_modules/spotify-web-api-node/node_modules/superagent/lib/node/unzip.js:53:12)
at Unzip.emit (events.js:400:28)
at endReadableNT (internal/streams/readable.js:1334:12)
at processTicksAndRejections (internal/process/task_queues.js:82:21)
服务器端代码:
//variables and imports
//openai api config code....
//fastify configuration code....
// Spotify configuration
const SPOTIFY_CLIENT_ID = process.env.SPOTIFY_CLIENT_ID;
const SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET;
const SPOTIFY_REDIRECT_URI = process.env.SPOTIFY_REDIRECT_URI;
const SPOTIFY_AUTH_SCOPES =
'user-read-private user-read-email playlist-modify-public playlist-modify-private';
const SpotifyWebApi = require('spotify-web-api-node');
const spotifyApi = new SpotifyWebApi({
clientId: SPOTIFY_CLIENT_ID,
clientSecret: SPOTIFY_CLIENT_SECRET,
redirectUri: SPOTIFY_REDIRECT_URI,
});
//search and extract songs from Spotify code...
// Utility function to generate a random string
function generateRandomString(length) {
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let text = "";
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
//search keyword refining code...
//create playlist and add tracks to it
async function createPlaylistAndAddTracks(userId, playlistName, tracks, accessToken) {
spotifyApi.setAccessToken(accessToken);
try {
const playlist = await new Promise((resolve, reject) => {
spotifyApi.createPlaylist(userId, playlistName, { public: true }, (err, data) => {
if (err) reject(err);
resolve(data);
});
});
const playlistId = playlist.body.id;
const trackUris = tracks.map((track) => track.uri);
await new Promise((resolve, reject) => {
spotifyApi.addTracksToPlaylist(playlistId, trackUris, (err, data) => {
if (err) reject(err);
resolve(data);
});
});
return playlistId;
} catch (error) {
console.error("Error creating playlist and adding tracks:", error);
throw error;
}
}
// Routes
// "/" get route
// "/" post route
fastify.get('/login', (req, reply) => {
const state = generateRandomString(16);
reply.setCookie("spotify_auth_state", state, {
path: "/",
maxAge: 3600, // 1 hour
httpOnly: true,
});
const authUrl =
'https://accounts.spotify.com/authorize' +
'?response_type=code' +
'&client_id=' + encodeURIComponent(SPOTIFY_CLIENT_ID) +
'&scope=' + encodeURIComponent(SPOTIFY_AUTH_SCOPES) +
'&redirect_uri=' + encodeURIComponent(SPOTIFY_REDIRECT_URI) +
'&state=' + state;
reply.redirect(authUrl);
});
// "user" get route code...
//"jockey" route for processing prompts and interacting with Spotify API
fastify.get('/jockey', function (request, reply) {
return reply.view('/src/pages/jockey.hbs');
});
//taking user input and generating keywords for use in SpotifyAPI
fastify.post("/jockey", async function (request, reply) {
const prompt = request.body.prompt;
const promptWithInstruction = `We have a user who wants to listen to music related to the theme: "${prompt}". Can you provide a comma-separated list of keywords or phrases that are relevant to this theme and could be used to search for music on Spotify?`;
try {
const result = await openai.createCompletion({
model: "text-davinci-003",
prompt: promptWithInstruction,
max_tokens: 2048,
temperature: 0.8,
});
const generatedText = result.data.choices[0].text.trim();
const keywords = extractKeywords(generatedText);
console.log("Generated Keywords:", keywords); //MILKSTEAK
const tracks = await searchAndExtractTracks(keywords, request.cookies.access_token);
console.log("Extracted tracks:", tracks);
// Get the user's ID
const userResponse = await spotifyApi.getMe();
const userId = userResponse.body.id;
// Create a new playlist and add the fetched tracks
const playlistId = await createPlaylistAndAddTracks(userId, request.cookies.access_token, tracks);
// Redirect to the /jockey page after processing the input
return reply.redirect("/jockey");
} catch (error) {
console.error(error);
return reply.code(500).send("Error generating response from OpenAI API");
}
});
fastify.get('/callback', async (req, reply) => {
const code = req.query.code;
const state = req.query.state;
const storedState = req.cookies.spotify_auth_state;
if (state === null || state !== storedState) {
reply.code(400).send('State mismatch');
} else {
reply.clearCookie("spotify_auth_state");
const tokenUrl = 'https://accounts.spotify.com/api/token';
try {
const response = await request.post(tokenUrl, {
form: {
code: code,
redirect_uri: SPOTIFY_REDIRECT_URI,
grant_type: 'authorization_code',
},
headers: {
Authorization:
'Basic ' +
Buffer.from(SPOTIFY_CLIENT_ID + ':' + SPOTIFY_CLIENT_SECRET).toString('base64'),
},
json: true,
});
// Save the access_token and refresh_token in cookies
const accessToken = response.access_token;
const refreshToken = response.refresh_token;
reply.setCookie("access_token", accessToken, {
path: "/",
maxAge: 3600, // 1 hour
httpOnly: true,
});
reply.setCookie("refresh_token", refreshToken, {
path: "/",
maxAge: 30 * 24 * 60 * 60, // 30 days
httpOnly: true,
});
reply.redirect('/jockey');
} catch (error) {
reply.code(400).send('Error: ' + error.message);
}
}
});
// let user logout and clear cookies/session "/logout" route
//fastify.listen code...
尝试在没有
createPlaylist
参数的情况下调用userId
。下面的解释。创建播放列表 (createPlaylist) 方法不再接受 userId 字符串作为其第一个参数。
该函数的新定义是
createPlaylist(name, options, callback)
并且您使用 userId 参数 createPlaylist(userId, playlistName, { public: true }, (err, data))
调用它,这意味着该函数将您的 userId
参数解释为 name
,playlistName
为 options
, {public: true}
作为回调,您的 (err,data)=>{}
回调当然会被忽略,因此当需要该回调时,它将尝试执行您的 {public: true}()
参数,认为它的回调当然会引发错误说明那 callback is not a function
因为 {public: true}
确实不是一个函数。