Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
29 changes: 21 additions & 8 deletions controllers/songController.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
const db = require('../db');
exports.getSongs = (req, res) => {
db.all('SELECT * FROM songs', (err, rows) => {
// Build dynamic WHERE clause based on query params
const filters = [];
const values = [];
['title', 'artist', 'genre', 'album', 'year', 'mood'].forEach(field => {
if (req.query[field]) {
filters.push(`${field} = ?`);
values.push(req.query[field]);
}
});
const where = filters.length ? `WHERE ${filters.join(' AND ')}` : '';
db.all(`SELECT * FROM songs ${where}`, values, (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows);
});
};

exports.addSong = (req, res) => {
const { title, artist, genre } = req.body;
const { title, artist, genre, album, year, mood } = req.body;
if (!title || !artist || !genre) {
return res.status(400).json({ error: 'Missing song fields' });
}

db.run('INSERT INTO songs (title, artist, genre) VALUES (?, ?, ?)', [title, artist, genre], function (err) {
if (err) return res.status(500).json({ error: err.message });
res.status(201).json({ id: this.lastID, title, artist, genre });
});
db.run(
'INSERT INTO songs (title, artist, genre, album, year, mood) VALUES (?, ?, ?, ?, ?, ?)',
[title, artist, genre, album || null, year || null, mood || null],
function (err) {
if (err) return res.status(500).json({ error: err.message });
res.status(201).json({ id: this.lastID, title, artist, genre, album, year, mood });
}
);
};

exports.getPlaylists = (req, res) => {
db.all('SELECT * FROM playlists', (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
Expand All @@ -27,7 +41,6 @@ exports.getPlaylists = (req, res) => {
exports.createPlaylist = (req, res) => {
const { name } = req.body;
if (!name) return res.status(400).json({ error: 'Playlist name is required' });

db.run('INSERT INTO playlists (name) VALUES (?)', [name], function (err) {
if (err) return res.status(500).json({ error: err.message });
res.status(201).json({ id: this.lastID, name });
Expand Down
24 changes: 23 additions & 1 deletion db.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ db.serialize(() => {

db.run(`CREATE TABLE IF NOT EXISTS playlists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
name TEXT,
user_id INTEGER,
FOREIGN KEY(user_id) REFERENCES users(id)
)`);

db.run(`CREATE TABLE IF NOT EXISTS playlist_songs (
Expand All @@ -20,6 +22,26 @@ db.serialize(() => {
FOREIGN KEY(playlist_id) REFERENCES playlists(id),
FOREIGN KEY(song_id) REFERENCES songs(id)
)`);

db.run(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
email TEXT UNIQUE,
password TEXT
)`);

db.run(`CREATE TABLE IF NOT EXISTS favorites (
user_id INTEGER,
song_id INTEGER,
PRIMARY KEY(user_id, song_id),
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(song_id) REFERENCES songs(id)
)`);

// Extend songs table for search/mood (if not already present)
db.run(`ALTER TABLE songs ADD COLUMN album TEXT`);
db.run(`ALTER TABLE songs ADD COLUMN year INTEGER`);
db.run(`ALTER TABLE songs ADD COLUMN mood TEXT`);
});

module.exports = db;
48 changes: 48 additions & 0 deletions favoritesController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const db = require('./db');

exports.listFavorites = (req, res) => {
const userId = parseInt(req.params.userId, 10);
if (!req.user || req.user.id !== userId) {
return res.status(403).json({ error: 'Forbidden' });
}
db.all(
`SELECT songs.* FROM favorites JOIN songs ON favorites.song_id = songs.id WHERE favorites.user_id = ?`,
[userId],
(err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows);
}
);
};

exports.addFavorite = (req, res) => {
const userId = parseInt(req.params.userId, 10);
const songId = parseInt(req.params.songId, 10);
if (!req.user || req.user.id !== userId) {
return res.status(403).json({ error: 'Forbidden' });
}
db.run(
`INSERT OR IGNORE INTO favorites (user_id, song_id) VALUES (?, ?)`,
[userId, songId],
function (err) {
if (err) return res.status(500).json({ error: err.message });
res.status(201).json({ message: 'Added to favorites' });
}
);
};

exports.removeFavorite = (req, res) => {
const userId = parseInt(req.params.userId, 10);
const songId = parseInt(req.params.songId, 10);
if (!req.user || req.user.id !== userId) {
return res.status(403).json({ error: 'Forbidden' });
}
db.run(
`DELETE FROM favorites WHERE user_id = ? AND song_id = ?`,
[userId, songId],
function (err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ message: 'Removed from favorites' });
}
);
};
16 changes: 16 additions & 0 deletions node_modules/.bin/mime

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions node_modules/.bin/mime.cmd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions node_modules/.bin/mime.ps1

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions node_modules/.bin/semver

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions node_modules/.bin/semver.cmd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions node_modules/.bin/semver.ps1

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading