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:
parent
6be01104cb
commit
bb6a1a962e
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
`);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue