add /t/ for future qobuz and soundcloud integration

This commit is contained in:
Eduard Prigoana 2026-02-10 22:10:40 +00:00
parent 8afb4a4c6d
commit b2fcfa1d52
7 changed files with 29 additions and 126 deletions

View file

@ -0,0 +1,2 @@
// functions/album/t/[id].js - Re-export handler from parent for /album/t/:id routes
export { onRequest } from '../[id].js';

View file

@ -0,0 +1,2 @@
// functions/artist/t/[id].js - Re-export handler from parent for /artist/t/:id routes
export { onRequest } from '../[id].js';

View file

@ -0,0 +1,2 @@
// functions/playlist/t/[id].js - Re-export handler from parent for /playlist/t/:id routes
export { onRequest } from '../[id].js';

View file

@ -0,0 +1,2 @@
// functions/track/t/[id].js - Re-export handler from parent for /track/t/:id routes
export { onRequest } from '../[id].js';

View file

@ -1,117 +0,0 @@
// functions/userplaylist/[id].js
// note that, since this NEEDS a playlist to yknow, be public, this only works for PUBLIC playlists (and you will need an account)
export async function onRequest(context) {
const { request, params, env } = context;
const userAgent = request.headers.get('User-Agent') || '';
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(
userAgent
);
const playlistId = params.id;
if (isBot && playlistId) {
try {
// 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);
// Fall back to user_playlists for private/owned playlists
if (!response.ok) {
pbUrl = `https://monodb.samidy.com/api/collections/user_playlists/records/${playlistId}`;
response = await fetch(pbUrl);
}
if (response.ok) {
let playlist = await response.json();
// 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');
const title = playlist.name || playlist.title || playlist.playlist_name || 'User Playlist';
let tracks = [];
try {
tracks = Array.isArray(playlist.tracks)
? playlist.tracks
: playlist.tracks
? JSON.parse(playlist.tracks)
: [];
} catch (e) {
console.error('Failed to parse tracks JSON', e);
}
const trackCount = tracks.length;
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';
const coverUrl = playlist.cover || playlist.image || playlist.playlist_cover || '';
if (coverUrl) {
if (coverUrl.startsWith('http')) {
imageUrl = coverUrl;
} else {
imageUrl = `https://monodb.samidy.com/api/files/${playlist.collectionId}/${playlist.id}/${coverUrl}`;
}
} else if (
tracks.length > 0 &&
typeof tracks[0] === 'object' &&
tracks[0].album &&
tracks[0].album.cover
) {
const cover = tracks[0].album.cover;
imageUrl = `https://resources.tidal.com/images/${cover.replace(/-/g, '/')}/1280x1280.jpg`;
}
const pageUrl = new URL(request.url).href;
const metaHtml = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<meta name="description" content="${description}">
<meta name="theme-color" content="#000000">
<meta property="og:site_name" content="Monochrome">
<meta property="og:title" content="${title}">
<meta property="og:description" content="${description}">
<meta property="og:image" content="${imageUrl}">
<meta property="og:type" content="music.playlist">
<meta property="og:url" content="${pageUrl}">
<meta property="music:song_count" content="${trackCount}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="${title}">
<meta name="twitter:description" content="${description}">
<meta name="twitter:image" content="${imageUrl}">
</head>
<body>
<h1>${title}</h1>
<p>${description}</p>
<img src="${imageUrl}" alt="Playlist Cover">
</body>
</html>
`;
return new Response(metaHtml, { headers: { 'content-type': 'text/html;charset=UTF-8' } });
}
} catch (error) {
console.error(`Error for user playlist ${playlistId}:`, error);
}
}
const url = new URL(request.url);
url.pathname = '/';
return env.ASSETS.fetch(new Request(url, request));
}

View file

@ -586,7 +586,11 @@ document.addEventListener('DOMContentLoaded', async () => {
const pathParts = window.location.pathname.split('/');
const albumIndex = pathParts.indexOf('album');
const albumId = albumIndex !== -1 ? pathParts[albumIndex + 1] : null;
let albumId = albumIndex !== -1 ? pathParts[albumIndex + 1] : null;
// Handle /album/t/ID format
if (albumId === 't') {
albumId = pathParts[albumIndex + 2];
}
if (!albumId) return;
@ -620,7 +624,11 @@ document.addEventListener('DOMContentLoaded', async () => {
const pathParts = window.location.pathname.split('/');
const albumIndex = pathParts.indexOf('album');
const albumId = albumIndex !== -1 ? pathParts[albumIndex + 1] : null;
let albumId = albumIndex !== -1 ? pathParts[albumIndex + 1] : null;
// Handle /album/t/ID format
if (albumId === 't') {
albumId = pathParts[albumIndex + 2];
}
if (!albumId) return;

View file

@ -29,18 +29,21 @@ export function createRouter(ui) {
const page = parts[0];
const param = parts.slice(1).join('/');
// Helper to strip /t/ prefix from params (for Tidal ID format like /album/t/123)
const stripTidalPrefix = (p) => (p.startsWith('t/') ? p.slice(2) : p);
switch (page) {
case 'search':
await ui.renderSearchPage(decodeURIComponent(param));
break;
case 'album':
await ui.renderAlbumPage(param);
await ui.renderAlbumPage(stripTidalPrefix(param));
break;
case 'artist':
await ui.renderArtistPage(param);
await ui.renderArtistPage(stripTidalPrefix(param));
break;
case 'playlist':
await ui.renderPlaylistPage(param, 'api');
await ui.renderPlaylistPage(stripTidalPrefix(param), 'api');
break;
case 'userplaylist':
await ui.renderPlaylistPage(param, 'user');
@ -49,13 +52,14 @@ export function createRouter(ui) {
await ui.renderFolderPage(param);
break;
case 'mix':
await ui.renderMixPage(param);
await ui.renderMixPage(stripTidalPrefix(param));
break;
case 'track':
if (param.startsWith('tracker-')) {
await ui.renderTrackerTrackPage(param);
const trackParam = stripTidalPrefix(param);
if (trackParam.startsWith('tracker-')) {
await ui.renderTrackerTrackPage(trackParam);
} else {
await ui.renderTrackPage(param);
await ui.renderTrackPage(trackParam);
}
break;
case 'library':