From 60fafb1c869a811987e12a2aeb16bd357b222e1b Mon Sep 17 00:00:00 2001 From: Advayp <69655599+Advayp@users.noreply.github.com> Date: Sat, 1 Feb 2025 23:12:14 -0800 Subject: [PATCH] Add script to initialize songs from Spotify API (#101) --- backend/scripts/initialize-songs.js | 125 ++++++++++++++++++++++++++++ frontend/scripts/reset-project.js | 84 ------------------- 2 files changed, 125 insertions(+), 84 deletions(-) create mode 100644 backend/scripts/initialize-songs.js delete mode 100755 frontend/scripts/reset-project.js diff --git a/backend/scripts/initialize-songs.js b/backend/scripts/initialize-songs.js new file mode 100644 index 0000000..8b017cb --- /dev/null +++ b/backend/scripts/initialize-songs.js @@ -0,0 +1,125 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +const accessToken = process.argv[2]; + +if (!accessToken) { + console.error('Access token is required'); + process.exit(1); +} + +const LIMIT = 50; +const PAGES = 2; + +const genres = [ + 'rap', + 'rock', + 'pop', + 'country', + 'edm', + 'jazz', + 'classical', + 'indie', + 'rock', + 'r&b', +]; + +const makeRequest = async (genre, page = 1) => { + const response = await fetch( + `https://api.spotify.com/v1/search?q=genre:${genre}&type=track&limit=${LIMIT}&offset=${ + (page - 1) * LIMIT + }`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + + const data = await response.json(); + return data.tracks.items; +}; + +const processSongs = (songs) => { + const uniqueNames = new Set(); + const includedSongs = []; + + songs.forEach((song) => { + if (uniqueNames.has(song.name)) { + return; + } + + uniqueNames.add(song.name); + includedSongs.push({ name: song.name, id: song.id }); + }); + + return { count: includedSongs.length, songs: includedSongs }; +}; + +const createGenre = async (genre) => { + const existingGenre = await prisma.genre.findUnique({ + where: { value: genre }, + }); + + if (existingGenre) { + console.log('Genre', genre, 'already exists'); + return existingGenre; + } + + console.log('Creating genre', genre); + return prisma.genre.create({ data: { value: genre.toLowerCase() } }); +}; + +const createSong = async (song, genre) => { + const existingSong = await prisma.song.findUnique({ + where: { trackID: song.id }, + }); + + if (existingSong) { + console.log('Song', song.name, 'already exists'); + return existingSong; + } + + const createdSong = await prisma.song.create({ + data: { + trackID: song.id, + genre: { connect: { value: genre } }, + }, + include: { genre: true }, + }); + console.log('Created song', song.name); + return createdSong; +}; + +const handleGenre = async (genre, page = 1) => { + const data = await makeRequest(genre, page); + const { count, songs } = processSongs(data); + + console.log('Found', count, 'unique songs for genre', genre); + + for (const song of songs) { + await createSong(song, genre); + } + + console.log('Finished genre', genre, 'with', count, 'songs'); +}; + +const run = async () => { + for (const genre of genres) { + await createGenre(genre); + } + + for (const genre of genres) { + for (let page = 1; page <= PAGES; page++) { + console.log('Processing genre', genre, 'page', page); + await handleGenre(genre, page); + console.log('Finished genre', genre, 'page', page); + } + console.log('Finished genre', genre); + } + + console.log('Finished all genres'); +}; + +run(); diff --git a/frontend/scripts/reset-project.js b/frontend/scripts/reset-project.js deleted file mode 100755 index 5f81463..0000000 --- a/frontend/scripts/reset-project.js +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * It moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example and creates a new /app directory with an index.tsx and _layout.tsx file. - * You can remove the `reset-project` script from package.json and safely delete this file after running it. - */ - -const fs = require("fs"); -const path = require("path"); - -const root = process.cwd(); -const oldDirs = ["app", "components", "hooks", "constants", "scripts"]; -const newDir = "app-example"; -const newAppDir = "app"; -const newDirPath = path.join(root, newDir); - -const indexContent = `import { Text, View } from "react-native"; - -export default function Index() { - return ( - - Edit app/index.tsx to edit this screen. - - ); -} -`; - -const layoutContent = `import { Stack } from "expo-router"; - -export default function RootLayout() { - return ; -} -`; - -const moveDirectories = async () => { - try { - // Create the app-example directory - await fs.promises.mkdir(newDirPath, { recursive: true }); - console.log(`šŸ“ /${newDir} directory created.`); - - // Move old directories to new app-example directory - for (const dir of oldDirs) { - const oldDirPath = path.join(root, dir); - const newDirPath = path.join(root, newDir, dir); - if (fs.existsSync(oldDirPath)) { - await fs.promises.rename(oldDirPath, newDirPath); - console.log(`āž”ļø /${dir} moved to /${newDir}/${dir}.`); - } else { - console.log(`āž”ļø /${dir} does not exist, skipping.`); - } - } - - // Create new /app directory - const newAppDirPath = path.join(root, newAppDir); - await fs.promises.mkdir(newAppDirPath, { recursive: true }); - console.log("\nšŸ“ New /app directory created."); - - // Create index.tsx - const indexPath = path.join(newAppDirPath, "index.tsx"); - await fs.promises.writeFile(indexPath, indexContent); - console.log("šŸ“„ app/index.tsx created."); - - // Create _layout.tsx - const layoutPath = path.join(newAppDirPath, "_layout.tsx"); - await fs.promises.writeFile(layoutPath, layoutContent); - console.log("šŸ“„ app/_layout.tsx created."); - - console.log("\nāœ… Project reset complete. Next steps:"); - console.log( - "1. Run `npx expo start` to start a development server.\n2. Edit app/index.tsx to edit the main screen.\n3. Delete the /app-example directory when you're done referencing it." - ); - } catch (error) { - console.error(`Error during script execution: ${error}`); - } -}; - -moveDirectories();