user playlists descriptions

This commit is contained in:
Eduard Prigoana 2026-02-09 20:34:40 +00:00
parent 62b83ea9e9
commit f73c75f668
6 changed files with 58 additions and 12 deletions

View file

@ -12,23 +12,31 @@ export async function onRequest(context) {
if (isBot && playlistId) { if (isBot && playlistId) {
try { try {
let pbUrl = `https://monodb.samidy.com/api/collections/user_playlists/records/${playlistId}`; // Try public_playlists collection first (for shared playlists)
let pbUrl = `https://monodb.samidy.com/api/collections/public_playlists/records?filter=(uuid='${playlistId}')`;
let response = await fetch(pbUrl); let response = await fetch(pbUrl);
// Fall back to user_playlists for private/owned playlists
if (!response.ok) { if (!response.ok) {
pbUrl = `https://monodb.samidy.com/api/collections/public_playlists/records?filter=(uuid='${playlistId}')`; pbUrl = `https://monodb.samidy.com/api/collections/user_playlists/records/${playlistId}`;
response = await fetch(pbUrl); response = await fetch(pbUrl);
} }
if (response.ok) { if (response.ok) {
let playlist = await response.json(); let playlist = await response.json();
if (playlist.items && Array.isArray(playlist.items) && playlist.items.length > 0) {
playlist = playlist.items[0]; // Handle public_playlists response (returns { items: [...] })
if (playlist.items && Array.isArray(playlist.items)) {
if (playlist.items.length > 0) {
playlist = playlist.items[0];
} else {
throw new Error('Playlist not found');
}
} }
if (!playlist) throw new Error('Playlist not found'); if (!playlist) throw new Error('Playlist not found');
const title = playlist.name || playlist.title || 'User Playlist'; const title = playlist.name || playlist.title || playlist.playlist_name || 'User Playlist';
let tracks = []; let tracks = [];
try { try {
tracks = Array.isArray(playlist.tracks) tracks = Array.isArray(playlist.tracks)
@ -41,14 +49,18 @@ export async function onRequest(context) {
} }
const trackCount = tracks.length; const trackCount = tracks.length;
const description = `User Playlist • ${trackCount} Tracks\nListen on Monochrome`; const playlistDescription = playlist.description || '';
const description = playlistDescription
? `${playlistDescription}\n${trackCount} Tracks • Listen on Monochrome`
: `User Playlist • ${trackCount} Tracks\nListen on Monochrome`;
let imageUrl = 'https://monochrome.samidy.com/assets/appicon.png'; let imageUrl = 'https://monochrome.samidy.com/assets/appicon.png';
if (playlist.cover) { const coverUrl = playlist.cover || playlist.image || playlist.playlist_cover || '';
if (playlist.cover.startsWith('http')) { if (coverUrl) {
imageUrl = playlist.cover; if (coverUrl.startsWith('http')) {
imageUrl = coverUrl;
} else { } else {
imageUrl = `https://monodb.samidy.com/api/files/${playlist.collectionId}/${playlist.id}/${playlist.cover}`; imageUrl = `https://monodb.samidy.com/api/files/${playlist.collectionId}/${playlist.id}/${coverUrl}`;
} }
} else if ( } else if (
tracks.length > 0 && tracks.length > 0 &&

View file

@ -352,6 +352,12 @@
here. (or any other service that allows directly linking to a file, but we recommend catbox due to here. (or any other service that allows directly linking to a file, but we recommend catbox due to
their long-term storage of your files) their long-term storage of your files)
</p> </p>
<textarea
id="playlist-description-input"
class="template-input"
placeholder="Description (optional)"
style="margin: 0.5rem 0; min-height: 80px; resize: vertical"
></textarea>
<br /> <br />
<div <div
id="csv-import-section" id="csv-import-section"

View file

@ -348,11 +348,17 @@ const syncManager = {
} }
if (!finalTitle) finalTitle = 'Untitled Playlist'; if (!finalTitle) finalTitle = 'Untitled Playlist';
let finalDescription = record.description || '';
if (!finalDescription && extraData && typeof extraData === 'object') {
finalDescription = extraData.description || '';
}
return { return {
...record, ...record,
id: record.uuid, id: record.uuid,
name: finalTitle, name: finalTitle,
title: finalTitle, title: finalTitle,
description: finalDescription,
cover: finalCover, cover: finalCover,
image: finalCover, image: finalCover,
tracks: tracks, tracks: tracks,
@ -384,11 +390,13 @@ const syncManager = {
image: playlist.cover, image: playlist.cover,
cover: playlist.cover, cover: playlist.cover,
playlist_cover: playlist.cover, playlist_cover: playlist.cover,
description: playlist.description || '',
tracks: JSON.stringify(playlist.tracks || []), tracks: JSON.stringify(playlist.tracks || []),
isPublic: true, isPublic: true,
data: { data: {
title: playlist.name, title: playlist.name,
cover: playlist.cover, cover: playlist.cover,
description: playlist.description || '',
}, },
}; };

View file

@ -649,6 +649,7 @@ document.addEventListener('DOMContentLoaded', async () => {
document.getElementById('playlist-modal-title').textContent = 'Create Playlist'; document.getElementById('playlist-modal-title').textContent = 'Create Playlist';
document.getElementById('playlist-name-input').value = ''; document.getElementById('playlist-name-input').value = '';
document.getElementById('playlist-cover-input').value = ''; document.getElementById('playlist-cover-input').value = '';
document.getElementById('playlist-description-input').value = '';
modal.dataset.editingId = ''; modal.dataset.editingId = '';
document.getElementById('csv-import-section').style.display = 'block'; document.getElementById('csv-import-section').style.display = 'block';
document.getElementById('csv-file-input').value = ''; document.getElementById('csv-file-input').value = '';
@ -696,6 +697,7 @@ document.addEventListener('DOMContentLoaded', async () => {
if (e.target.closest('#playlist-modal-save')) { if (e.target.closest('#playlist-modal-save')) {
const name = document.getElementById('playlist-name-input').value.trim(); const name = document.getElementById('playlist-name-input').value.trim();
const description = document.getElementById('playlist-description-input').value.trim();
const isPublic = document.getElementById('playlist-public-toggle')?.checked; const isPublic = document.getElementById('playlist-public-toggle')?.checked;
if (name) { if (name) {
@ -728,6 +730,7 @@ document.addEventListener('DOMContentLoaded', async () => {
if (playlist) { if (playlist) {
playlist.name = name; playlist.name = name;
playlist.cover = cover; playlist.cover = cover;
playlist.description = description;
await handlePublicStatus(playlist); await handlePublicStatus(playlist);
await db.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist)); await db.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
syncManager.syncUserPlaylist(playlist, 'update'); syncManager.syncUserPlaylist(playlist, 'update');
@ -817,7 +820,7 @@ document.addEventListener('DOMContentLoaded', async () => {
console.log(`Added ${tracks.length} tracks (including pending)`); console.log(`Added ${tracks.length} tracks (including pending)`);
} }
db.createPlaylist(name, tracks, cover).then(async (playlist) => { db.createPlaylist(name, tracks, cover, description).then(async (playlist) => {
await handlePublicStatus(playlist); await handlePublicStatus(playlist);
// Update DB again with isPublic flag // Update DB again with isPublic flag
await db.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist)); await db.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
@ -842,6 +845,7 @@ document.addEventListener('DOMContentLoaded', async () => {
document.getElementById('playlist-modal-title').textContent = 'Edit Playlist'; document.getElementById('playlist-modal-title').textContent = 'Edit Playlist';
document.getElementById('playlist-name-input').value = playlist.name; document.getElementById('playlist-name-input').value = playlist.name;
document.getElementById('playlist-cover-input').value = playlist.cover || ''; document.getElementById('playlist-cover-input').value = playlist.cover || '';
document.getElementById('playlist-description-input').value = playlist.description || '';
// Set Public Toggle // Set Public Toggle
const publicToggle = document.getElementById('playlist-public-toggle'); const publicToggle = document.getElementById('playlist-public-toggle');
@ -886,6 +890,7 @@ document.addEventListener('DOMContentLoaded', async () => {
document.getElementById('playlist-modal-title').textContent = 'Edit Playlist'; document.getElementById('playlist-modal-title').textContent = 'Edit Playlist';
document.getElementById('playlist-name-input').value = playlist.name; document.getElementById('playlist-name-input').value = playlist.name;
document.getElementById('playlist-cover-input').value = playlist.cover || ''; document.getElementById('playlist-cover-input').value = playlist.cover || '';
document.getElementById('playlist-description-input').value = playlist.description || '';
const publicToggle = document.getElementById('playlist-public-toggle'); const publicToggle = document.getElementById('playlist-public-toggle');
const shareBtn = document.getElementById('playlist-share-btn'); const shareBtn = document.getElementById('playlist-share-btn');

View file

@ -466,13 +466,14 @@ export class MusicDatabase {
} }
// User Playlists API // User Playlists API
async createPlaylist(name, tracks = [], cover = '') { async createPlaylist(name, tracks = [], cover = '', description = '') {
const id = crypto.randomUUID(); const id = crypto.randomUUID();
const playlist = { const playlist = {
id: id, id: id,
name: name, name: name,
tracks: tracks.map((t) => this._minifyItem('track', { ...t, addedAt: Date.now() })), tracks: tracks.map((t) => this._minifyItem('track', { ...t, addedAt: Date.now() })),
cover: cover, cover: cover,
description: description,
createdAt: Date.now(), createdAt: Date.now(),
updatedAt: Date.now(), updatedAt: Date.now(),
numberOfTracks: tracks.length, numberOfTracks: tracks.length,
@ -667,6 +668,18 @@ export class MusicDatabase {
return playlist; return playlist;
} }
async updatePlaylistDescription(playlistId, newDescription) {
const playlist = await this.performTransaction('user_playlists', 'readonly', (store) => store.get(playlistId));
if (!playlist) throw new Error('Playlist not found');
playlist.description = newDescription;
playlist.updatedAt = Date.now();
await this.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
this._dispatchPlaylistSync('update', playlist);
return playlist;
}
async updatePlaylistTracks(playlistId, tracks) { async updatePlaylistTracks(playlistId, tracks) {
const db = await this.open(); const db = await this.open();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View file

@ -649,6 +649,7 @@ export async function showAddToPlaylistModal(track) {
document.getElementById('playlist-modal-title').textContent = 'Create Playlist'; document.getElementById('playlist-modal-title').textContent = 'Create Playlist';
document.getElementById('playlist-name-input').value = ''; document.getElementById('playlist-name-input').value = '';
document.getElementById('playlist-cover-input').value = ''; document.getElementById('playlist-cover-input').value = '';
document.getElementById('playlist-description-input').value = '';
createModal.dataset.editingId = ''; createModal.dataset.editingId = '';
document.getElementById('csv-import-section').style.display = 'none'; document.getElementById('csv-import-section').style.display = 'none';
@ -1005,6 +1006,7 @@ export async function handleTrackAction(
document.getElementById('playlist-modal-title').textContent = 'Create Playlist'; document.getElementById('playlist-modal-title').textContent = 'Create Playlist';
document.getElementById('playlist-name-input').value = ''; document.getElementById('playlist-name-input').value = '';
document.getElementById('playlist-cover-input').value = ''; document.getElementById('playlist-cover-input').value = '';
document.getElementById('playlist-description-input').value = '';
createModal.dataset.editingId = ''; createModal.dataset.editingId = '';
document.getElementById('csv-import-section').style.display = 'none'; document.getElementById('csv-import-section').style.display = 'none';