chore: simplify playlist visibility logic and update dependencies

- Remove `VISIBILITY_RANK` and auto-upgrade logic from admin routes
- Simplify `playlists.js` by removing `getEffectiveVisibility` and complex `NOT EXISTS` subqueries in list queries
- Update `public.js` to use direct playlist visibility instead of effective visibility
- Bump `better-sqlite3` to v11.10.0 and set project version to 1.0.0
This commit is contained in:
Brian Fertig 2026-05-02 16:38:09 -06:00
parent 6be01104cb
commit bb6a1a962e
6 changed files with 7 additions and 106 deletions

6
package-lock.json generated
View File

@ -1,15 +1,15 @@
{
"name": "tunes",
"version": "0.1.0",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tunes",
"version": "0.1.0",
"version": "1.0.0",
"dependencies": {
"argon2": "^0.40.1",
"better-sqlite3": "^11.3.0",
"better-sqlite3": "^11.10.0",
"better-sqlite3-session-store": "^0.1.0",
"cropperjs": "^1.6.2",
"csrf-sync": "^4.0.3",

View File

@ -10,7 +10,7 @@
},
"dependencies": {
"argon2": "^0.40.1",
"better-sqlite3": "^11.3.0",
"better-sqlite3": "^11.10.0",
"better-sqlite3-session-store": "^0.1.0",
"cropperjs": "^1.6.2",
"csrf-sync": "^4.0.3",

View File

@ -41,7 +41,6 @@ const songFields = upload.fields([{ name: 'audio', maxCount: 1 }, { name: 'cover
const playlistFields = upload.fields([{ name: 'cover', maxCount: 1 }]);
const VISIBILITY_VALUES = ['public', 'logged_in', 'vip', 'private'];
const VISIBILITY_RANK = { public: 0, logged_in: 1, vip: 2, private: 3 };
// For uploads, all text fields are optional — we'll fill blanks from ID3 tags.
// The edit form enforces title/artist via a stricter schema below.
@ -204,9 +203,6 @@ module.exports = function adminRoutes(csrfProtection) {
const song = songs.findById(songId);
if (!song) continue;
playlists.addTrack(playlistId, songId);
if (VISIBILITY_RANK[song.visibility] > VISIBILITY_RANK[playlist.visibility]) {
playlists.setVisibility(playlistId, song.visibility);
}
}
res.json({ ok: true, added: songIds.length });
} catch (err) { next(err); }
@ -417,9 +413,6 @@ module.exports = function adminRoutes(csrfProtection) {
const song = songs.findById(songId);
if (!song) continue;
playlists.addTrack(created.id, songId);
if (VISIBILITY_RANK[song.visibility] > VISIBILITY_RANK[created.visibility]) {
playlists.setVisibility(created.id, song.visibility);
}
}
playlists.updateTopGenres(created.id);
req.flash('success', 'Playlist created.');
@ -498,10 +491,6 @@ module.exports = function adminRoutes(csrfProtection) {
if (!playlist || !song) return next();
if (!canManage(playlist, req.session.user)) return forbidden(next);
playlists.addTrack(id, songId);
// Auto-upgrade playlist visibility if the song is more restrictive.
if (VISIBILITY_RANK[song.visibility] > VISIBILITY_RANK[playlist.visibility]) {
playlists.setVisibility(id, song.visibility);
}
playlists.updateTopGenres(id);
res.redirect(`/mymusic/playlists/${id}/edit`);
} catch (err) { next(err); }

View File

@ -120,10 +120,8 @@ router.get('/playlists/:slug', (req, res, next) => {
if (!playlist) return next();
const userId = req.session.user?.id || null;
const isVip = !!(req.session.user?.isVip || req.session.user?.role === 'admin');
// Use effective visibility (most restrictive of playlist + all its tracks).
const effectiveVisibility = playlists.getEffectiveVisibility(playlist.id);
if (!canView(effectiveVisibility, playlist.createdBy, userId, isVip)) {
return denyAccess(req, res, next, effectiveVisibility);
if (!canView(playlist.visibility, playlist.createdBy, userId, isVip)) {
return denyAccess(req, res, next, playlist.visibility);
}
social.enrichPlaylists([playlist], userId);
const tracks = playlists.getTracks(playlist.id).filter(t => canView(t.visibility, t.uploadedBy, userId, isVip) && (!t.isNsfw || !!userId));

View File

@ -14,58 +14,29 @@ const setTopGenresStmt = db.prepare('UPDATE playlists SET top_genres = ? WHERE i
const deleteStmt = db.prepare('DELETE FROM playlists WHERE id = ?');
const findByIdStmt = db.prepare('SELECT * FROM playlists WHERE id = ?');
// For list queries, effective visibility = max(playlist.visibility, most restrictive track).
// Guests only see playlists where nothing in them is logged_in, vip, or private.
const listGuestStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.visibility = 'public'
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility IN ('logged_in', 'vip', 'private')
)
ORDER BY p.created_at DESC
`);
// Logged-in non-VIP users see public/logged_in playlists where nothing in them is vip or private.
const listUserStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.visibility IN ('public', 'logged_in')
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility IN ('vip', 'private')
)
ORDER BY p.created_at DESC
`);
// VIP users: public + logged_in + vip playlists (no private tracks)
const listVipStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.visibility IN ('public', 'logged_in', 'vip')
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility = 'private'
)
ORDER BY p.created_at DESC
`);
const featuredVipStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.visibility IN ('public', 'logged_in', 'vip')
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility = 'private'
)
ORDER BY RANDOM() LIMIT ?
`);
const listPublicByUserVipStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.created_by = ? AND p.visibility IN ('public', 'logged_in', 'vip')
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility = 'private'
)
ORDER BY p.created_at DESC
`);
const forSongVipStmt = db.prepare(`
@ -80,41 +51,14 @@ const listByUserStmt = db.prepare('SELECT * FROM playlists WHERE created_by = ?
const featuredGuestStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.visibility = 'public'
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility IN ('logged_in', 'vip', 'private')
)
ORDER BY RANDOM() LIMIT ?
`);
const featuredUserStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.visibility IN ('public', 'logged_in')
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility IN ('vip', 'private')
)
ORDER BY RANDOM() LIMIT ?
`);
// Compute the single most-restrictive visibility for one playlist (stored + tracks).
const effectiveVisibilityStmt = db.prepare(`
SELECT
CASE
WHEN p.visibility = 'private'
OR EXISTS (SELECT 1 FROM playlist_songs ps JOIN songs s ON s.id = ps.song_id WHERE ps.playlist_id = p.id AND s.visibility = 'private')
THEN 'private'
WHEN p.visibility = 'vip'
OR EXISTS (SELECT 1 FROM playlist_songs ps JOIN songs s ON s.id = ps.song_id WHERE ps.playlist_id = p.id AND s.visibility = 'vip')
THEN 'vip'
WHEN p.visibility = 'logged_in'
OR EXISTS (SELECT 1 FROM playlist_songs ps JOIN songs s ON s.id = ps.song_id WHERE ps.playlist_id = p.id AND s.visibility = 'logged_in')
THEN 'logged_in'
ELSE 'public'
END AS effective_visibility
FROM playlists p WHERE p.id = ?
`);
const trackGenresStmt = db.prepare(`
SELECT s.genre FROM playlist_songs ps
@ -273,11 +217,6 @@ function findById(id) {
return toView(findByIdStmt.get(id));
}
function getEffectiveVisibility(id) {
const row = effectiveVisibilityStmt.get(id);
return row ? row.effective_visibility : null;
}
function listVisible(loggedIn = false, isVip = false) {
return (isVip ? listVipStmt : loggedIn ? listUserStmt : listGuestStmt).all().map(toView);
}
@ -322,21 +261,11 @@ function setOrder(playlistId, songIds) {
const listPublicByUserGuestStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.created_by = ? AND p.visibility = 'public'
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility IN ('logged_in', 'vip', 'private')
)
ORDER BY p.created_at DESC
`);
const listPublicByUserLoggedStmt = db.prepare(`
SELECT p.* FROM playlists p
WHERE p.created_by = ? AND p.visibility IN ('public', 'logged_in')
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility IN ('vip', 'private')
)
ORDER BY p.created_at DESC
`);
@ -385,7 +314,7 @@ function getSimilar(playlistId, loggedIn = false, isVip = false, limit = 5) {
}
module.exports = {
create, update, setCover, setVisibility, remove, findById, findBySlug, getEffectiveVisibility,
create, update, setCover, setVisibility, remove, findById, findBySlug,
listVisible, listAll, listByUser, listPublicByUser, featured,
getTracks, trackCount, addTrack, removeTrack, setOrder, getPlaylistsForSong,
updateTopGenres, reindexAllGenres, getSimilar,

View File

@ -124,33 +124,18 @@ const likedPlaylistsGuestStmt = db.prepare(`
SELECT p.* FROM playlist_likes pl
JOIN playlists p ON p.id = pl.playlist_id
WHERE pl.user_id = ? AND p.visibility = 'public'
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility IN ('logged_in', 'vip', 'private')
)
ORDER BY pl.created_at DESC
`);
const likedPlaylistsUserStmt = db.prepare(`
SELECT p.* FROM playlist_likes pl
JOIN playlists p ON p.id = pl.playlist_id
WHERE pl.user_id = ? AND p.visibility IN ('public', 'logged_in')
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility IN ('vip', 'private')
)
ORDER BY pl.created_at DESC
`);
const likedPlaylistsVipStmt = db.prepare(`
SELECT p.* FROM playlist_likes pl
JOIN playlists p ON p.id = pl.playlist_id
WHERE pl.user_id = ? AND p.visibility IN ('public', 'logged_in', 'vip')
AND NOT EXISTS (
SELECT 1 FROM playlist_songs ps
JOIN songs s ON s.id = ps.song_id
WHERE ps.playlist_id = p.id AND s.visibility = 'private'
)
ORDER BY pl.created_at DESC
`);