add /t/ for future qobuz and soundcloud integration
This commit is contained in:
parent
8afb4a4c6d
commit
b2fcfa1d52
7 changed files with 29 additions and 126 deletions
2
functions/album/t/[id].js
Normal file
2
functions/album/t/[id].js
Normal 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';
|
||||
2
functions/artist/t/[id].js
Normal file
2
functions/artist/t/[id].js
Normal 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';
|
||||
2
functions/playlist/t/[id].js
Normal file
2
functions/playlist/t/[id].js
Normal 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';
|
||||
2
functions/track/t/[id].js
Normal file
2
functions/track/t/[id].js
Normal 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';
|
||||
|
|
@ -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));
|
||||
}
|
||||
12
js/app.js
12
js/app.js
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
18
js/router.js
18
js/router.js
|
|
@ -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':
|
||||
|
|
|
|||
Loading…
Reference in a new issue