112 lines
3.3 KiB
JavaScript
112 lines
3.3 KiB
JavaScript
|
const express = require('express');
|
||
|
const app = express();
|
||
|
const path = require('path');
|
||
|
const SoundCloud = require('soundcloud-scraper');
|
||
|
const client = new SoundCloud.Client();
|
||
|
const fs = require('fs');
|
||
|
const PORT = process.env.PORT || 6767;
|
||
|
|
||
|
let tracks = []; // Store the tracks globally
|
||
|
const CACHE_FILE = path.join(__dirname, 'cache.json');
|
||
|
|
||
|
// Helper function to create a slug from track title
|
||
|
function generateSlug(title) {
|
||
|
return title
|
||
|
.toLowerCase() // Ensure lowercase
|
||
|
.replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric characters with hyphens
|
||
|
.replace(/(^-|-$)/g, ''); // Remove leading and trailing hyphens
|
||
|
}
|
||
|
|
||
|
// Helper function to read the cache
|
||
|
function readCache() {
|
||
|
if (fs.existsSync(CACHE_FILE)) {
|
||
|
const data = fs.readFileSync(CACHE_FILE, 'utf8');
|
||
|
return JSON.parse(data);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// Helper function to save cache
|
||
|
function saveCache(data) {
|
||
|
fs.writeFileSync(CACHE_FILE, JSON.stringify(data), 'utf8');
|
||
|
}
|
||
|
|
||
|
// Fetch playlist tracks from SoundCloud
|
||
|
async function fetchPlaylist() {
|
||
|
const playlist = await client.getPlaylist('https://soundcloud.com/snxraven/sets/raven-scott-metal');
|
||
|
|
||
|
return playlist.tracks.map(track => {
|
||
|
const slug = generateSlug(track.title); // Generate slug here
|
||
|
return {
|
||
|
title: track.title,
|
||
|
description: track.description || 'No description available',
|
||
|
url: track.url,
|
||
|
playCount: track.playCount || 0,
|
||
|
publishedAt: track.publishedAt || new Date().toISOString(),
|
||
|
slug // Save the slug in the track object
|
||
|
};
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Get tracks from cache or SoundCloud
|
||
|
async function getTracks(fetch = false) {
|
||
|
const cache = readCache();
|
||
|
const oneWeekInMs = 7 * 24 * 60 * 60 * 1000; // One week in milliseconds
|
||
|
const now = Date.now();
|
||
|
|
||
|
if (fetch || !cache || (now - cache.timestamp) > oneWeekInMs) {
|
||
|
// Fetch fresh tracks from SoundCloud
|
||
|
tracks = await fetchPlaylist();
|
||
|
saveCache({ tracks, timestamp: now });
|
||
|
} else {
|
||
|
// Load from cache
|
||
|
tracks = cache.tracks;
|
||
|
|
||
|
// Ensure slug generation in case it is missing from the cached data
|
||
|
tracks = tracks.map(track => {
|
||
|
if (!track.slug) {
|
||
|
track.slug = generateSlug(track.title);
|
||
|
}
|
||
|
return track;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Sort by playCount first, then by publishedAt
|
||
|
tracks.sort((a, b) => {
|
||
|
if (b.playCount !== a.playCount) return b.playCount - a.playCount;
|
||
|
return new Date(b.publishedAt) - new Date(a.publishedAt);
|
||
|
});
|
||
|
|
||
|
return tracks;
|
||
|
}
|
||
|
|
||
|
// Serve static files from public directory
|
||
|
app.use(express.static(path.join(__dirname, 'public')));
|
||
|
|
||
|
// Set EJS as templating engine
|
||
|
app.set('view engine', 'ejs');
|
||
|
app.set('views', path.join(__dirname, 'views'));
|
||
|
|
||
|
// Home page route
|
||
|
app.get('/', async (req, res) => {
|
||
|
const allTracks = await getTracks();
|
||
|
res.render('index', { tracks: allTracks });
|
||
|
});
|
||
|
|
||
|
// Individual track page route
|
||
|
app.get('/track/:slug', async (req, res) => {
|
||
|
const allTracks = await getTracks();
|
||
|
const track = allTracks.find(t => t.slug === req.params.slug);
|
||
|
|
||
|
if (!track) {
|
||
|
return res.status(404).send('Track not found');
|
||
|
}
|
||
|
|
||
|
res.render('track', { track });
|
||
|
});
|
||
|
|
||
|
// Listen on the specified port
|
||
|
app.listen(PORT, () => {
|
||
|
console.log(`Server is running on port ${PORT}`);
|
||
|
});
|