Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 153 additions & 3 deletions backend/src/controllers/album.controller.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
const Album = require("../models/album.model")
const Album = require("../models/album.model");
const Song = require("../models/song.model");
const cloudinary = require("../lib/cloudinary");

const getPublicId = (url) => {
if (!url) return null;
const parts = url.split("/");
const fileName = parts.pop();
const publicId = fileName.split(".")[0];
return publicId;
};

const uploadToCloudinary = async (file) => {
try {
const result = await cloudinary.uploader.upload(file.tempFilePath, {
resource_type: "auto",
});
return result.secure_url;
} catch (err) {
console.log("Error in uploadToCloudinary:", err);
throw new Error(err.message || "Cloudinary upload failed");
}
};

const AllAlbums = async (req, res, next) => {
try {
const albums = await Album.find();
const filter = {};

// If user=true, only show their own albums
if (req.query.user === "true" && req.auth?.userId) {
filter.creator = req.auth.userId;
} else if (!req.query.all === "true") {
// Default behavior if not asking for "all" (and not an admin) could be to still filter by user
// or just allow discovery if intended.
// For now, let's keep it restricted to the creator if they are in a management context.
// But if it's for discovery (HomePage), they might not pass user=true.
// However, the dashboard ALWAYS passes user=true.
}

const albums = await Album.find(filter);
res.status(200).json({
success: true,
message: "All Albums",
Expand Down Expand Up @@ -32,7 +67,122 @@ const AllAlbumsById = async (req, res, next) => {
}
}

const createAlbum = async (req, res, next) => {
try {
if (!req.files || !req.files.imageFile) {
return res.status(400).json({ success: false, message: "Image file is required" });
}

const { title, artist, releaseYear } = req.body;
const creator = req.auth.userId;

if (!title || !artist) {
return res.status(400).json({ success: false, message: "Title and artist are required" });
}

const imageUrl = await uploadToCloudinary(req.files.imageFile);

const album = new Album({
title,
artist,
imageUrl,
releaseYear: releaseYear || new Date().getFullYear(),
songs: [],
creator,
});

await album.save();
res.status(201).json({ success: true, message: "Album created successfully", album });
} catch (error) {
next(error);
}
};

const deleteAlbum = async (req, res, next) => {
try {
const { id } = req.params;
const userId = req.auth.userId;

const album = await Album.findById(id).populate("songs");
if (!album) {
return res.status(404).json({ success: false, message: "Album not found" });
}

if (album.creator && album.creator !== userId) {
const { clerkClient } = require('@clerk/express');
const currentUser = await clerkClient.users.getUser(userId);
const primaryEmail = currentUser?.primaryEmailAddress?.emailAddress;
const isAdmin = process.env.ADMIN_EMAIL && primaryEmail && process.env.ADMIN_EMAIL === primaryEmail;

if (!isAdmin) {
return res.status(403).json({ message: "Unauthorized to delete this album" });
}
}

// Delete songs and media
for (const song of album.songs) {
try {
if (song.audioUrl) await cloudinary.uploader.destroy(getPublicId(song.audioUrl), { resource_type: "video" });
if (song.imageUrl) await cloudinary.uploader.destroy(getPublicId(song.imageUrl));
} catch (err) {
console.error("Error deleting song media during album deletion:", err);
}
}

if (album.imageUrl) {
try {
await cloudinary.uploader.destroy(getPublicId(album.imageUrl));
} catch (err) {
console.error("Error deleting album image:", err);
}
}

await Song.deleteMany({ albumId: id });
await Album.findByIdAndDelete(id);

res.status(200).json({ success: true, message: "Album deleted successfully" });
} catch (error) {
next(error);
}
};

const updateAlbum = async (req, res, next) => {
try {
const { id } = req.params;
const { title, artist, releaseYear } = req.body;
const userId = req.auth.userId;

const album = await Album.findById(id);
if (!album) return res.status(404).json({ success: false, message: "Album not found" });

if (album.creator && album.creator !== userId) {
return res.status(403).json({ message: "Unauthorized to update this album" });
}

const updatedData = { title, artist, releaseYear: releaseYear || new Date().getFullYear() };

if (req.files && req.files.imageFile) {
if (album.imageUrl) {
try {
await cloudinary.uploader.destroy(getPublicId(album.imageUrl));
} catch (err) {
console.error("Cloudinary old album image deletion failed:", err);
}
}
updatedData.imageUrl = await uploadToCloudinary(req.files.imageFile);
}

const updatedAlbum = await Album.findByIdAndUpdate(id, updatedData, { new: true });
res.status(200).json({ success: true, message: "Album updated successfully", album: updatedAlbum });
} catch (error) {
next(error);
}
};

module.exports = {
AllAlbums,
AllAlbumsById
AllAlbumsById,
createAlbum,
deleteAlbum,
updateAlbum,
}
166 changes: 166 additions & 0 deletions backend/src/controllers/playlist.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
const Playlist = require("../models/playlist.model");
const Song = require("../models/song.model");

const createPlaylist = async (req, res, next) => {
try {
const { name, description, imageUrl } = req.body;
const creator = req.auth.userId;

if (!name) {
return res.status(400).json({ message: "Playlist name is required" });
}

const playlist = new Playlist({
name,
description,
imageUrl,
creator,
songs: [],
});

await playlist.save();
res.status(201).json(playlist);
} catch (error) {
next(error);
}
};

const getUserPlaylists = async (req, res, next) => {
try {
const userId = req.auth.userId;
const playlists = await Playlist.find({ creator: userId }).populate("songs");
res.status(200).json(playlists);
} catch (error) {
next(error);
}
};

const getPlaylistById = async (req, res, next) => {
try {
const { id } = req.params;
const playlist = await Playlist.findById(id).populate("songs");

if (!playlist) {
return res.status(404).json({ message: "Playlist not found" });
}

res.status(200).json(playlist);
} catch (error) {
next(error);
}
};

const updatePlaylist = async (req, res, next) => {
try {
const { id } = req.params;
const { name, description, imageUrl, songs } = req.body;
const userId = req.auth.userId;

const playlist = await Playlist.findById(id);

if (!playlist) {
return res.status(404).json({ message: "Playlist not found" });
}

if (playlist.creator !== userId) {
return res.status(403).json({ message: "Unauthorized to update this playlist" });
}

if (name) playlist.name = name;
if (description !== undefined) playlist.description = description;
if (imageUrl) playlist.imageUrl = imageUrl;
if (songs) playlist.songs = songs; // This handles reordering

await playlist.save();
res.status(200).json(playlist);
} catch (error) {
next(error);
}
};

const deletePlaylist = async (req, res, next) => {
try {
const { id } = req.params;
const userId = req.auth.userId;

const playlist = await Playlist.findById(id);

if (!playlist) {
return res.status(404).json({ message: "Playlist not found" });
}

if (playlist.creator !== userId) {
return res.status(403).json({ message: "Unauthorized to delete this playlist" });
}

await Playlist.findByIdAndDelete(id);
res.status(200).json({ message: "Playlist deleted successfully" });
} catch (error) {
next(error);
}
};

const addSongToPlaylist = async (req, res, next) => {
try {
const { id } = req.params;
const { songId } = req.body;
const userId = req.auth.userId;

const playlist = await Playlist.findById(id);

if (!playlist) {
return res.status(404).json({ message: "Playlist not found" });
}

if (playlist.creator !== userId) {
return res.status(403).json({ message: "Unauthorized to modify this playlist" });
}

if (playlist.songs.includes(songId)) {
return res.status(400).json({ message: "Song already in playlist" });
}

playlist.songs.push(songId);
await playlist.save();

const updatedPlaylist = await Playlist.findById(id).populate("songs");
res.status(200).json(updatedPlaylist);
} catch (error) {
next(error);
}
};

const removeSongFromPlaylist = async (req, res, next) => {
try {
const { id, songId } = req.params;
const userId = req.auth.userId;

const playlist = await Playlist.findById(id);

if (!playlist) {
return res.status(404).json({ message: "Playlist not found" });
}

if (playlist.creator !== userId) {
return res.status(403).json({ message: "Unauthorized to modify this playlist" });
}

playlist.songs = playlist.songs.filter(s => s.toString() !== songId);
await playlist.save();

const updatedPlaylist = await Playlist.findById(id).populate("songs");
res.status(200).json(updatedPlaylist);
} catch (error) {
next(error);
}
};

module.exports = {
createPlaylist,
getUserPlaylists,
getPlaylistById,
updatePlaylist,
deletePlaylist,
addSongToPlaylist,
removeSongFromPlaylist,
};
Loading
Loading