fix pages functions
This commit is contained in:
parent
fd4267a03e
commit
79fd04cef0
10 changed files with 537 additions and 8 deletions
36
functions/about/index.js
Normal file
36
functions/about/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export async function onRequest(context) {
|
||||
const { request, env } = context;
|
||||
const pageUrl = request.url;
|
||||
|
||||
const metaHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Monochrome Music | About</title>
|
||||
<meta name="description" content="A minimalist music streaming application">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="Monochrome Music | About">
|
||||
<meta property="og:description" content="A minimalist music streaming application">
|
||||
<meta property="og:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Monochrome Music | About">
|
||||
<meta name="twitter:description" content="A minimalist music streaming application">
|
||||
<meta name="twitter:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Monochrome Music | About</h1>
|
||||
<p>A minimalist music streaming application</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
36
functions/donate/index.js
Normal file
36
functions/donate/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export async function onRequest(context) {
|
||||
const { request, env } = context;
|
||||
const pageUrl = request.url;
|
||||
|
||||
const metaHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Monochrome Music | Donate</title>
|
||||
<meta name="description" content="A minimalist music streaming application">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="Monochrome Music | Donate">
|
||||
<meta property="og:description" content="A minimalist music streaming application">
|
||||
<meta property="og:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Monochrome Music | Donate">
|
||||
<meta name="twitter:description" content="A minimalist music streaming application">
|
||||
<meta name="twitter:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Monochrome Music | Donate</h1>
|
||||
<p>A minimalist music streaming application</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
37
functions/library/index.js
Normal file
37
functions/library/index.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
export async function onRequest(context) {
|
||||
const { request, env } = context;
|
||||
const url = new URL(request.url);
|
||||
const pageUrl = request.url;
|
||||
|
||||
const metaHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Monochrome Music | Library</title>
|
||||
<meta name="description" content="A minimalist music streaming application">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="Monochrome Music | Library">
|
||||
<meta property="og:description" content="A minimalist music streaming application">
|
||||
<meta property="og:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Monochrome Music | Library">
|
||||
<meta name="twitter:description" content="A minimalist music streaming application">
|
||||
<meta name="twitter:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Monochrome Music | Library</h1>
|
||||
<p>A minimalist music streaming application</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
36
functions/parties/index.js
Normal file
36
functions/parties/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export async function onRequest(context) {
|
||||
const { request, env } = context;
|
||||
const pageUrl = request.url;
|
||||
|
||||
const metaHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Monochrome Music | Listening Parties</title>
|
||||
<meta name="description" content="Listen to music with your friends">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="Monochrome Music | Listening Parties">
|
||||
<meta property="og:description" content="Listen to music with your friends">
|
||||
<meta property="og:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Monochrome Music | Listening Parties">
|
||||
<meta name="twitter:description" content="Listen to music with your friends">
|
||||
<meta name="twitter:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Monochrome Music | Listening Parties</h1>
|
||||
<p>Listen to music with your friends</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
36
functions/recent/index.js
Normal file
36
functions/recent/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export async function onRequest(context) {
|
||||
const { request, env } = context;
|
||||
const pageUrl = request.url;
|
||||
|
||||
const metaHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Monochrome Music | Recent</title>
|
||||
<meta name="description" content="A minimalist music streaming application">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="Monochrome Music | Recent">
|
||||
<meta property="og:description" content="A minimalist music streaming application">
|
||||
<meta property="og:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Monochrome Music | Recent">
|
||||
<meta name="twitter:description" content="A minimalist music streaming application">
|
||||
<meta name="twitter:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Monochrome Music | Recent</h1>
|
||||
<p>A minimalist music streaming application</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
36
functions/settings/index.js
Normal file
36
functions/settings/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export async function onRequest(context) {
|
||||
const { request, env } = context;
|
||||
const pageUrl = request.url;
|
||||
|
||||
const metaHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Monochrome Music | Settings</title>
|
||||
<meta name="description" content="A minimalist music streaming application">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="Monochrome Music | Settings">
|
||||
<meta property="og:description" content="A minimalist music streaming application">
|
||||
<meta property="og:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Monochrome Music | Settings">
|
||||
<meta name="twitter:description" content="A minimalist music streaming application">
|
||||
<meta name="twitter:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Monochrome Music | Settings</h1>
|
||||
<p>A minimalist music streaming application</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
101
functions/unreleased/[sheetId].js
Normal file
101
functions/unreleased/[sheetId].js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// functions/unreleased/[sheetId].js
|
||||
|
||||
const ARTISTS_NDJSON_URL = 'https://assets.artistgrid.cx/artists.ndjson';
|
||||
const ASSETS_BASE_URL = 'https://assets.artistgrid.cx';
|
||||
|
||||
function getSheetId(url) {
|
||||
if (!url) return null;
|
||||
const match = url.match(/spreadsheets\/d\/([a-zA-Z0-9-_]+)/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function normalizeArtistName(name) {
|
||||
return name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
}
|
||||
|
||||
async function loadArtistsData() {
|
||||
try {
|
||||
const response = await fetch(ARTISTS_NDJSON_URL);
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
const text = await response.text();
|
||||
return text
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter((line) => line.trim())
|
||||
.map((line) => {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter((item) => item !== null);
|
||||
} catch (e) {
|
||||
console.error('Failed to load Artists List:', e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
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|telegrambot|linkedinbot|mastodon|signal|snapchat|redditbot|skypeuripreview|viberbot|linebot|embedly|quora|outbrain|tumblr|duckduckbot|yandexbot|rogerbot|showyoubot|kakaotalk|naverbot|seznambot|mediapartners|adsbot|petalbot|applebot|ia_archiver/i.test(
|
||||
userAgent
|
||||
);
|
||||
const sheetId = params.sheetId;
|
||||
|
||||
if (isBot && sheetId) {
|
||||
try {
|
||||
const artists = await loadArtistsData();
|
||||
const artist = artists.find((a) => getSheetId(a.url) === sheetId);
|
||||
|
||||
if (artist && artist.name) {
|
||||
const normalizedName = normalizeArtistName(artist.name);
|
||||
const imageUrl = `${ASSETS_BASE_URL}/${normalizedName}.webp`;
|
||||
const pageUrl = new URL(request.url).href;
|
||||
const title = `${artist.name} | Unreleased`;
|
||||
const description = `Stream unreleased music by ${artist.name} on Monochrome`;
|
||||
|
||||
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="profile">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<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>${artist.name}</h1>
|
||||
<p>${description}</p>
|
||||
<img src="${imageUrl}" alt="${artist.name}">
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error generating meta tags for unreleased artist ${sheetId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
url.pathname = '/';
|
||||
return env.ASSETS.fetch(new Request(url, request));
|
||||
}
|
||||
131
functions/unreleased/[sheetId]/[projectName].js
Normal file
131
functions/unreleased/[sheetId]/[projectName].js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// functions/unreleased/[sheetId]/[projectName].js
|
||||
|
||||
const ARTISTS_NDJSON_URL = 'https://assets.artistgrid.cx/artists.ndjson';
|
||||
const ASSETS_BASE_URL = 'https://assets.artistgrid.cx';
|
||||
const TRACKER_API_ENDPOINTS = ['https://trackerapi-1.artistgrid.cx/get/', 'https://trackerapi-2.artistgrid.cx/get/'];
|
||||
|
||||
function getSheetId(url) {
|
||||
if (!url) return null;
|
||||
const match = url.match(/spreadsheets\/d\/([a-zA-Z0-9-_]+)/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function normalizeArtistName(name) {
|
||||
return name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
}
|
||||
|
||||
function transformImageUrl(url) {
|
||||
if (!url) return url;
|
||||
return url.replace('https://s3.sad.ovh/trackerapi/', 'https://r2.artistgrid.cx/');
|
||||
}
|
||||
|
||||
async function loadArtistsData() {
|
||||
try {
|
||||
const response = await fetch(ARTISTS_NDJSON_URL);
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
const text = await response.text();
|
||||
return text
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter((line) => line.trim())
|
||||
.map((line) => {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter((item) => item !== null);
|
||||
} catch (e) {
|
||||
console.error('Failed to load Artists List:', e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchTrackerData(sheetId) {
|
||||
for (const baseUrl of TRACKER_API_ENDPOINTS) {
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}${sheetId}`);
|
||||
if (!response.ok) continue;
|
||||
const data = await response.json();
|
||||
if (data.eras) {
|
||||
for (const eraName in data.eras) {
|
||||
const era = data.eras[eraName];
|
||||
if (era.image) {
|
||||
era.image = transformImageUrl(era.image);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
} catch (e) {
|
||||
console.warn(`Failed to fetch from ${baseUrl}, trying next...`);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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|telegrambot|linkedinbot|linkedinbot|mastodon|signal|snapchat|redditbot|skypeuripreview|viberbot|linebot|embedly|quora|outbrain|tumblr|duckduckbot|yandexbot|rogerbot|showyoubot|kakaotalk|naverbot|seznambot|mediapartners|adsbot|petalbot|applebot|ia_archiver/i.test(
|
||||
userAgent
|
||||
);
|
||||
const sheetId = params.sheetId;
|
||||
const projectName = params.projectName ? decodeURIComponent(params.projectName) : null;
|
||||
|
||||
if (isBot && sheetId && projectName) {
|
||||
try {
|
||||
const artists = await loadArtistsData();
|
||||
const artist = artists.find((a) => getSheetId(a.url) === sheetId);
|
||||
const trackerData = await fetchTrackerData(sheetId);
|
||||
|
||||
if (artist && artist.name && trackerData && trackerData.eras) {
|
||||
const era = trackerData.eras[projectName];
|
||||
const imageUrl = era && era.image ? era.image : 'https://monochrome.tf/assets/appicon.png';
|
||||
const pageUrl = new URL(request.url).href;
|
||||
const title = `${projectName} - ${artist.name}`;
|
||||
const description = `Stream ${projectName} by ${artist.name} on Monochrome`;
|
||||
|
||||
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.album">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<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="${projectName} cover">
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error generating meta tags for unreleased project ${sheetId}/${projectName}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
url.pathname = '/';
|
||||
return env.ASSETS.fetch(new Request(url, request));
|
||||
}
|
||||
36
functions/unreleased/index.js
Normal file
36
functions/unreleased/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export async function onRequest(context) {
|
||||
const { request, env } = context;
|
||||
const pageUrl = request.url;
|
||||
|
||||
const metaHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Monochrome Music | Unreleased</title>
|
||||
<meta name="description" content="Stream unreleased music on Monochrome. Provided by Artistgrid.">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="Monochrome Music | Unreleased">
|
||||
<meta property="og:description" content="Stream unreleased music on Monochrome. Provided by Artistgrid.">
|
||||
<meta property="og:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Monochrome Music | Unreleased">
|
||||
<meta name="twitter:description" content="Stream unreleased music on Monochrome. Provided by Artistgrid.">
|
||||
<meta name="twitter:image" content="https://monochrome.tf/assets/appicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Monochrome Music | Unreleased</h1>
|
||||
<p>Stream unreleased music on Monochrome. Provided by Artistgrid.</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
}
|
||||
|
|
@ -3,6 +3,54 @@
|
|||
const POCKETBASE_URL = 'https://data.samidy.xyz';
|
||||
const PUBLIC_COLLECTION = 'public_playlists';
|
||||
|
||||
function safeParseTracks(tracksData) {
|
||||
if (!tracksData) return [];
|
||||
if (Array.isArray(tracksData)) return tracksData;
|
||||
if (typeof tracksData === 'string') {
|
||||
try {
|
||||
return JSON.parse(tracksData);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function parseDuration(durationStr) {
|
||||
if (!durationStr || durationStr === 'N/A' || typeof durationStr !== 'string') return 0;
|
||||
const parts = durationStr.split(':');
|
||||
if (parts.length === 2) {
|
||||
return parseInt(parts[0]) * 60 + parseInt(parts[1]);
|
||||
}
|
||||
if (parts.length === 3) {
|
||||
return parseInt(parts[0]) * 3600 + parseInt(parts[1]) * 60 + parseInt(parts[2]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function calculatePlaylistDuration(tracks) {
|
||||
let totalSeconds = 0;
|
||||
for (const track of tracks) {
|
||||
const duration = track.duration || track.durationSeconds || 0;
|
||||
if (typeof duration === 'number') {
|
||||
totalSeconds += duration;
|
||||
} else if (typeof duration === 'string') {
|
||||
totalSeconds += parseDuration(duration);
|
||||
}
|
||||
}
|
||||
return totalSeconds;
|
||||
}
|
||||
|
||||
function formatDuration(seconds) {
|
||||
if (!seconds || seconds <= 0) return '0 min';
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
if (hours > 0) {
|
||||
return `${hours} hr ${minutes} min`;
|
||||
}
|
||||
return `${minutes} min`;
|
||||
}
|
||||
|
||||
export async function onRequest(context) {
|
||||
const { request, params, env } = context;
|
||||
const userAgent = request.headers.get('User-Agent') || '';
|
||||
|
|
@ -37,14 +85,10 @@ export async function onRequest(context) {
|
|||
(extraData && (extraData.title || extraData.name)) ||
|
||||
'Untitled Playlist';
|
||||
|
||||
let tracks = [];
|
||||
try {
|
||||
tracks = record.tracks ? JSON.parse(record.tracks) : [];
|
||||
} catch {
|
||||
tracks = [];
|
||||
}
|
||||
|
||||
let tracks = safeParseTracks(record.tracks);
|
||||
const trackCount = tracks.length;
|
||||
const totalDuration = calculatePlaylistDuration(tracks);
|
||||
const durationStr = formatDuration(totalDuration);
|
||||
|
||||
let rawCover = record.image || record.cover || record.playlist_cover || '';
|
||||
if (!rawCover && extraData && typeof extraData === 'object') {
|
||||
|
|
@ -70,7 +114,7 @@ export async function onRequest(context) {
|
|||
imageUrl = 'https://monochrome.tf/assets/appicon.png';
|
||||
}
|
||||
|
||||
const description = `Playlist • ${trackCount} Tracks\nListen on Monochrome`;
|
||||
const description = `Playlist • ${trackCount} Tracks • ${durationStr}\nListen on Monochrome`;
|
||||
const pageUrl = new URL(request.url).href;
|
||||
|
||||
const metaHtml = `
|
||||
|
|
|
|||
Loading…
Reference in a new issue