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();