Add new scripts and update album data

- Added a new script command "fetch-links" to package.json for fetching song links.
- Updated tsconfig.json to include "bun-types" in the types array.
- Removed the VIEW_TRANSITIONS_GUIDE.md file.
- Modified album track IDs to be more descriptive and added Spotify URLs for each track in list.ts.
- Updated ListTypes.ts to include a new field for Spotify URLs in the Track type.
- Cleaned up the AlbumDetail component by removing commented-out code for the album header.
This commit is contained in:
m4x809 2025-10-25 01:02:10 +02:00
parent 6633b7858f
commit a8097d84fc
Signed by: m4x809
SSH key fingerprint: SHA256:YCoFF78p2DUP94EnCScqLwldjkKDwdKSZq3r8p/6EiU
12 changed files with 879 additions and 297 deletions

View file

@ -1,100 +0,0 @@
# View Transitions Implementation Guide
## Overview
This project now uses the native **View Transitions API** to create smooth animations between pages, including browser back/forward navigation. The implementation is based on best practices from the [nmn.sh](https://github.com/nmn/nmn.sh) repository.
## What Changed
### 1. CSS Configuration (`src/index.css`)
Added comprehensive view transition CSS rules:
```css
/* Enable smooth cross-document view transitions (browser navigation) */
@view-transition {
navigation: auto;
}
/* Default transition for all elements */
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.3s;
animation-timing-function: ease-in-out;
}
```
**Key Feature**: `@view-transition { navigation: auto; }` enables transitions to work with native browser navigation (back/forward buttons, URL changes), not just client-side routing.
### 2. Component Updates
**AlbumCard.tsx** and **album/[albumId]/page.tsx**:
Changed from React `ViewTransition` component wrapper:
```tsx
// ❌ Old approach (only works with client-side routing)
<ViewTransition name={`album-card-image-${album.id}`}>
<Image src={image} alt={label} />
</ViewTransition>
```
To native CSS `viewTransitionName` property:
```tsx
// ✅ New approach (works with all navigation)
<Image
src={image}
alt={label}
style={{ viewTransitionName: `album-card-image-${album.id}` }}
/>
```
### 3. TypeScript Support
Added type definitions in `src/types/view-transitions.d.ts` to extend React's `CSSProperties` interface with `viewTransitionName` support.
## How It Works
### Client-Side Routing
- Uses `next-view-transitions` package
- `<Link>` component from the package automatically triggers view transitions
- Smooth animations between pages when clicking links
### Browser Navigation (Back/Forward)
- Native `@view-transition { navigation: auto; }` CSS rule
- Browser automatically captures before/after states
- Smooth transitions even with browser buttons or URL changes
- **No JavaScript required** for basic transitions
### Named Transitions
Elements with matching `viewTransitionName` values will morph smoothly between pages:
- Album images: `album-card-image-{id}`
- Album titles: `album-card-title-{id}`
- Release dates: `album-card-release-date-{id}`
## Browser Support
- **Chrome/Edge**: Full support (v111+)
- **Safari**: Support in v18+ (macOS Sonoma, iOS 17)
- **Firefox**: In development
- **Fallback**: Graceful degradation to instant navigation
## Testing
1. **Client-side navigation**: Click any album card → smooth transition
2. **Browser back button**: Press back → smooth transition
3. **Direct URL change**: Type URL directly → smooth transition on supported browsers
## Benefits Over Previous Implementation
1. ✅ **Works with browser navigation** (back/forward buttons)
2. ✅ **Works with direct URL changes**
3. ✅ **Better performance** (native browser API)
4. ✅ **More reliable** (less JavaScript, more standards-based)
5. ✅ **Progressive enhancement** (works without JS)
## References
- [View Transitions API (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
- [nmn.sh repository](https://github.com/nmn/nmn.sh)
- [Chrome View Transitions Guide](https://developer.chrome.com/docs/web-platform/view-transitions/)

0
data/.gitkeep Normal file
View file

27
data/fetched-songs.json Normal file
View file

@ -0,0 +1,27 @@
{
"hybrid-theory-1": true,
"hybrid-theory-2": true,
"hybrid-theory-3": true,
"hybrid-theory-4": true,
"hybrid-theory-5": true,
"hybrid-theory-6": true,
"hybrid-theory-7": true,
"hybrid-theory-8": true,
"hybrid-theory-9": true,
"hybrid-theory-10": true,
"hybrid-theory-11": true,
"hybrid-theory-12": true,
"meteora-1": true,
"meteora-2": true,
"meteora-3": true,
"meteora-4": true,
"meteora-5": true,
"meteora-6": true,
"meteora-7": true,
"meteora-8": true,
"meteora-9": true,
"meteora-10": true,
"meteora-11": true,
"meteora-12": true,
"meteora-13": true
}

152
data/song-links.json Normal file
View file

@ -0,0 +1,152 @@
{
"hybrid-theory-1": {
"spotify": "https://open.spotify.com/track/4HvcbAR8LisalXKt7cpqE1",
"youtube": "https://www.youtube.com/watch?v=vjVkXlxsO8Q",
"youtubeMusic": "https://music.youtube.com/watch?v=vjVkXlxsO8Q",
"appleMusic": "https://geo.music.apple.com/de/album/_/528436018?i=528436568&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-2": {
"spotify": "https://open.spotify.com/track/4bYLTrlcqctyHck3fjhMgW",
"youtube": "https://www.youtube.com/watch?v=4qlCC1GOwFw",
"youtubeMusic": "https://music.youtube.com/watch?v=4qlCC1GOwFw",
"appleMusic": "https://geo.music.apple.com/de/album/_/590431776?i=590431778&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-3": {
"spotify": "https://open.spotify.com/track/1Q9QN6dHEDeUegsySY012f",
"youtube": "https://www.youtube.com/watch?v=M8UTS2iFXOo",
"youtubeMusic": "https://music.youtube.com/watch?v=M8UTS2iFXOo",
"appleMusic": "https://geo.music.apple.com/de/album/_/528436018?i=528437421&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-4": {
"spotify": "https://open.spotify.com/track/214LJVTqrkUOGemWDGHJpr",
"youtube": "https://www.youtube.com/watch?v=jZSPAp8kCl4",
"youtubeMusic": "https://music.youtube.com/watch?v=jZSPAp8kCl4",
"appleMusic": "https://geo.music.apple.com/de/album/_/590431776?i=590431781&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-5": {
"spotify": "https://open.spotify.com/track/0X576XiCW9uEtko6f6VkcU",
"youtube": "https://www.youtube.com/watch?v=Gd9OhYroLN0",
"youtubeMusic": "https://music.youtube.com/watch?v=Gd9OhYroLN0",
"appleMusic": "https://geo.music.apple.com/de/album/_/591534774?i=591534780&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-6": {
"spotify": "https://open.spotify.com/track/5tkHje8FGSF258gi869GcV",
"youtube": "https://www.youtube.com/watch?v=ig-fyQqf510",
"youtubeMusic": "https://music.youtube.com/watch?v=ig-fyQqf510",
"appleMusic": "https://geo.music.apple.com/de/album/_/528436018?i=528437609&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-7": {
"spotify": "https://open.spotify.com/track/4SJoEhpI6C61wtUfxSHMGc",
"youtube": "https://www.youtube.com/watch?v=wWBp-nlGX1o",
"youtubeMusic": "https://music.youtube.com/watch?v=wWBp-nlGX1o",
"appleMusic": "https://geo.music.apple.com/de/album/_/528436018?i=528437611&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-8": {
"spotify": "https://open.spotify.com/track/7q115ia4fQn9zonjpexWsY",
"youtube": "https://www.youtube.com/watch?v=eVTXPUF4Oz4",
"youtubeMusic": "https://music.youtube.com/watch?v=eVTXPUF4Oz4",
"appleMusic": "https://geo.music.apple.com/de/album/_/528436018?i=528437613&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-9": {
"spotify": "https://open.spotify.com/track/1B8WdDScvobpFsZLfdmIE1",
"youtube": "https://www.youtube.com/watch?v=3t2WkCudwfY",
"youtubeMusic": "https://music.youtube.com/watch?v=3t2WkCudwfY",
"appleMusic": "https://geo.music.apple.com/de/album/_/528436018?i=528437708&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-10": {
"spotify": "https://open.spotify.com/track/3JAu2BH8sSI46rJGzQerb6",
"youtube": "https://www.youtube.com/watch?v=HNCgBuI2eJc",
"youtubeMusic": "https://music.youtube.com/watch?v=HNCgBuI2eJc",
"appleMusic": "https://geo.music.apple.com/de/album/_/528436018?i=528437709&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-11": {
"spotify": "https://open.spotify.com/track/15voDM9pGkwEvHBUecpcmo",
"youtube": "https://www.youtube.com/watch?v=qqC5sdsHLq8",
"youtubeMusic": "https://music.youtube.com/watch?v=qqC5sdsHLq8",
"appleMusic": "https://geo.music.apple.com/de/album/_/528436018?i=528437710&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"hybrid-theory-12": {
"spotify": "https://open.spotify.com/track/4HlMOXxwF9SYG1RoJxEjio",
"youtube": "https://www.youtube.com/watch?v=Ve1LNJEIKUE",
"youtubeMusic": "https://music.youtube.com/watch?v=Ve1LNJEIKUE",
"appleMusic": "https://geo.music.apple.com/de/album/_/590431776?i=590431790&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-1": {
"spotify": "https://open.spotify.com/track/4hbpxoMCp62bd1sdy7XehS",
"youtube": "https://www.youtube.com/watch?v=U6R-twDkrcI",
"youtubeMusic": "https://music.youtube.com/watch?v=U6R-twDkrcI",
"appleMusic": "https://geo.music.apple.com/de/album/_/590423275?i=590423280&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-2": {
"spotify": "https://open.spotify.com/track/6HZdbb05lEXLvcmee3ZXO2",
"youtube": "https://www.youtube.com/watch?v=oWfGOVWrueo",
"youtubeMusic": "https://music.youtube.com/watch?v=oWfGOVWrueo",
"appleMusic": "https://geo.music.apple.com/de/album/_/1668484895?i=1668485277&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-3": {
"spotify": "https://open.spotify.com/track/3agtg0x11wPvLIWkYR39nZ",
"youtube": "https://www.youtube.com/watch?v=zsCD5XCu6CM",
"youtubeMusic": "https://music.youtube.com/watch?v=zsCD5XCu6CM",
"appleMusic": "https://geo.music.apple.com/de/album/_/528435845?i=528437020&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-4": {
"spotify": "https://open.spotify.com/track/4zP2e2aIzOZGEFTq1MDJmm",
"youtube": "https://www.youtube.com/watch?v=NjdgcHdzvac",
"youtubeMusic": "https://music.youtube.com/watch?v=NjdgcHdzvac",
"appleMusic": "https://geo.music.apple.com/de/album/_/590423275?i=590423283&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-5": {
"spotify": "https://open.spotify.com/track/3R9NKZ4jmxNjOFihSF5s7f",
"youtube": "https://www.youtube.com/watch?v=oMals9XXQY8",
"youtubeMusic": "https://music.youtube.com/watch?v=oMals9XXQY8",
"appleMusic": "https://geo.music.apple.com/de/album/_/590423275?i=590423284&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-6": {
"spotify": "https://open.spotify.com/track/17bgialGAwoiGj1STY4cnR",
"youtube": "https://www.youtube.com/watch?v=U5zdmjVeQzE",
"youtubeMusic": "https://music.youtube.com/watch?v=U5zdmjVeQzE",
"appleMusic": "https://geo.music.apple.com/de/album/_/590423275?i=590423285&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-7": {
"spotify": "https://open.spotify.com/track/4Yf5bqU3NK4kNOypcrLYwU",
"youtube": "https://www.youtube.com/watch?v=LYU-8IFcDPw",
"youtubeMusic": "https://music.youtube.com/watch?v=LYU-8IFcDPw",
"appleMusic": "https://geo.music.apple.com/de/album/_/528435845?i=528437024&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-8": {
"spotify": "https://open.spotify.com/track/2DrMcGNKEY0FZqjMovGaCm",
"youtube": "https://www.youtube.com/watch?v=6dEAeCHQrBs",
"youtubeMusic": "https://music.youtube.com/watch?v=6dEAeCHQrBs",
"appleMusic": "https://geo.music.apple.com/de/album/_/528435845?i=528437025&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-9": {
"spotify": "https://open.spotify.com/track/6n8TMVyFKoUmDc4apxceRD",
"youtube": "https://www.youtube.com/watch?v=v2H4l9RpkwM",
"youtubeMusic": "https://music.youtube.com/watch?v=v2H4l9RpkwM",
"appleMusic": "https://geo.music.apple.com/de/album/_/528435845?i=528437026&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-10": {
"spotify": "https://open.spotify.com/track/60IkVf7UfQXmt5CwkpcX8a",
"youtube": "https://www.youtube.com/watch?v=YLHpvjrFpe0",
"youtubeMusic": "https://music.youtube.com/watch?v=YLHpvjrFpe0",
"appleMusic": "https://geo.music.apple.com/de/album/_/528435845?i=528437027&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-11": {
"spotify": "https://open.spotify.com/track/4m1P1BSg9nLxaWbvPhNdhw",
"youtube": "https://www.youtube.com/watch?v=QJ87793QXes",
"youtubeMusic": "https://music.youtube.com/watch?v=QJ87793QXes",
"appleMusic": "https://geo.music.apple.com/de/album/_/590423275?i=590423550&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-12": {
"spotify": "https://open.spotify.com/track/3sbezh9MMCQrr4KY7zw13j",
"youtube": "https://www.youtube.com/watch?v=J1KqQYsUYIk",
"youtubeMusic": "https://music.youtube.com/watch?v=J1KqQYsUYIk",
"appleMusic": "https://geo.music.apple.com/de/album/_/528435845?i=528437513&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
},
"meteora-13": {
"spotify": "https://open.spotify.com/track/2nLtzopw4rPReszdYBJU6h",
"youtube": "https://www.youtube.com/watch?v=kXYiU_JCYtU",
"youtubeMusic": "https://music.youtube.com/watch?v=kXYiU_JCYtU",
"appleMusic": "https://geo.music.apple.com/de/album/_/528435845?i=528437514&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m"
}
}

View file

@ -7,7 +7,8 @@
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "eslint ." "lint": "eslint .",
"fetch-links": "bun run scripts/fetch-song-links.ts"
}, },
"dependencies": { "dependencies": {
"@mantine/carousel": "^8.3.5", "@mantine/carousel": "^8.3.5",
@ -23,6 +24,7 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.36.0", "@eslint/js": "^9.36.0",
"@types/bun": "^1.3.1",
"@types/node": "^24.6.0", "@types/node": "^24.6.0",
"@types/react": "^19.2.2", "@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2", "@types/react-dom": "^19.2.2",

74
scripts/README.md Normal file
View file

@ -0,0 +1,74 @@
# Song Links Fetcher
This script fetches streaming links (Spotify, YouTube, YouTube Music, Apple Music) for all Linkin Park songs in the project using the SongLink API.
## Features
- 🎵 Fetches links for all songs from the `list.ts` file using their Spotify URLs
- ⏱️ Respects API rate limit (10 requests per minute)
- 💾 Caches already-fetched songs to avoid redundant API calls
- 📁 Outputs both TypeScript and JSON files
- 📊 Shows progress and summary statistics
- ⏭️ Automatically skips songs without Spotify URLs
- 🎨 Automatically formats all generated files with Prettier
## Usage
Run the script with:
```bash
bun run fetch-links
```
## Output Files
1. **`src/lib/songLinks.ts`** - TypeScript file with exported `songLinks` object
2. **`data/song-links.json`** - JSON file with all song links
3. **`data/fetched-songs.json`** - Cache file tracking which songs have been fetched
## Data Structure
The generated TypeScript file exports a `songLinks` object with this structure:
```typescript
{
"hybrid-theory-1": {
"spotify": "https://open.spotify.com/track/...",
"youtube": "https://www.youtube.com/watch?v=...",
"youtubeMusic": "https://music.youtube.com/watch?v=...",
"appleMusic": "https://music.apple.com/..."
},
// ... more songs
}
```
## API Information
- **API**: [SongLink/Odesli API](https://linktree.notion.site/API-d0ebe08a5e304a55928405eb682f6741)
- **Endpoint**: `https://api.song.link/v1-alpha.1/links`
- **Parameters**:
- `url`: Spotify track URL (encoded)
- `userCountry`: DE (Germany)
- `songIfSingle`: true (for better matching)
- **Rate Limit**: 10 requests per minute
- **Delay between requests**: 6 seconds
## Requirements
Each track in `list.ts` must have a `__SPOTIFY_URL__` field with a valid Spotify track URL. Tracks without this field will be automatically skipped.
## Resumable
If the script is interrupted or fails for any song, you can simply run it again. It will:
- Skip already-fetched songs (based on the cache file)
- Continue fetching remaining songs
- Preserve all previously fetched data
## Clearing Cache
To re-fetch all songs from scratch, delete the cache file:
```bash
rm data/fetched-songs.json
```

243
scripts/fetch-song-links.ts Normal file
View file

@ -0,0 +1,243 @@
#!/usr/bin/env bun
import { join } from "path";
import { $ } from "bun";
import { albums } from "../src/lib/list";
// Configuration
const API_BASE = "https://api.song.link/v1-alpha.1/links";
const RATE_LIMIT_DELAY = 6000; // 6 seconds (10 requests per minute)
const USER_COUNTRY = "DE"; // Germany
const DATA_DIR = join(import.meta.dir, "../data");
const CACHE_FILE = join(DATA_DIR, "fetched-songs.json");
const OUTPUT_TS_FILE = join(import.meta.dir, "../src/lib/songLinks.ts");
const OUTPUT_JSON_FILE = join(DATA_DIR, "song-links.json");
interface SongLinks {
spotify?: string;
youtube?: string;
youtubeMusic?: string;
appleMusic?: string;
}
interface SongLinksData {
[songId: string]: SongLinks;
}
interface CachedSongs {
[songId: string]: boolean;
}
// Delay function for rate limiting
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Load cached songs
async function loadCache(): Promise<CachedSongs> {
const file = Bun.file(CACHE_FILE);
const exists = await file.exists();
if (!exists) {
console.log("No cache file found, starting fresh...");
return {};
}
try {
return await file.json();
} catch {
console.log("Invalid cache file, starting fresh...");
return {};
}
}
// Load existing song links
async function loadExistingSongLinks(): Promise<SongLinksData> {
const file = Bun.file(OUTPUT_JSON_FILE);
const exists = await file.exists();
if (!exists) {
console.log("No existing song links found, starting fresh...");
return {};
}
try {
return await file.json();
} catch {
console.log("Invalid song links file, starting fresh...");
return {};
}
}
// Ensure data directory exists
async function ensureDataDir(): Promise<void> {
await Bun.write(join(DATA_DIR, ".gitkeep"), "");
}
// Save cache
async function saveCache(cache: CachedSongs): Promise<void> {
await ensureDataDir();
await Bun.write(CACHE_FILE, JSON.stringify(cache, null, 2));
}
// Save song links to JSON
async function saveSongLinksJSON(songLinks: SongLinksData): Promise<void> {
await ensureDataDir();
await Bun.write(OUTPUT_JSON_FILE, JSON.stringify(songLinks, null, 2));
}
// Save song links to TypeScript file
async function saveSongLinksTS(songLinks: SongLinksData): Promise<void> {
const tsContent = `// This file is auto-generated by scripts/fetch-song-links.ts
// Do not edit manually
// Last updated: ${new Date().toISOString()}
// Run: bun run fetch-links
export interface SongLinks {
spotify?: string;
youtube?: string;
youtubeMusic?: string;
appleMusic?: string;
}
export interface SongLinksData {
[songId: string]: SongLinks;
}
export const songLinks: SongLinksData = ${JSON.stringify(songLinks, null, 2)};
`;
await Bun.write(OUTPUT_TS_FILE, tsContent);
}
// Fetch song links from API
async function fetchSongLinks(spotifyUrl: string, songName: string): Promise<SongLinks | null> {
try {
// Encode the Spotify URL
const encodedUrl = encodeURIComponent(spotifyUrl);
// Use the url parameter with songIfSingle for better matching
const url = `${API_BASE}?url=${encodedUrl}&userCountry=${USER_COUNTRY}&songIfSingle=true`;
console.log(` Fetching: ${songName}`);
console.log(` Spotify URL: ${spotifyUrl}`);
const response = await fetch(url);
if (!response.ok) {
const errorText = await response.text();
console.error(` ❌ Failed to fetch (${response.status}): ${errorText}`);
return null;
}
const data = await response.json();
// Extract links from the response
const links: SongLinks = {
spotify: data.linksByPlatform?.spotify?.url || undefined,
youtube: data.linksByPlatform?.youtube?.url || undefined,
youtubeMusic: data.linksByPlatform?.youtubeMusic?.url || undefined,
appleMusic: data.linksByPlatform?.appleMusic?.url || undefined,
};
console.log(` ✓ Success!`);
return links;
} catch (error) {
console.error(` ❌ Error:`, error);
return null;
}
}
// Main function
async function main() {
console.log("🎵 Starting song link fetcher...\n");
// Load existing data
const cache = await loadCache();
const songLinks = await loadExistingSongLinks();
let requestCount = 0;
let successCount = 0;
let skipCount = 0;
let failCount = 0;
// Process all albums and tracks
for (const album of albums) {
console.log(`\n📀 Album: ${album.label}`);
for (const track of album.tracks) {
const songId = track.id;
const songName = track.label;
const spotifyUrl = track.__SPOTIFY_URL__;
// Skip if already fetched
if (cache[songId]) {
console.log(` ⏭️ Skipping: ${songName} (already fetched)`);
skipCount++;
continue;
}
// Skip if no Spotify URL
if (!spotifyUrl) {
console.log(` ⏭️ Skipping: ${songName} (no Spotify URL)`);
skipCount++;
continue;
}
// Add delay before request (except for first request)
if (requestCount > 0) {
console.log(` ⏳ Waiting ${RATE_LIMIT_DELAY / 1000}s (rate limit)...`);
await delay(RATE_LIMIT_DELAY);
}
// Fetch links
const links = await fetchSongLinks(spotifyUrl, songName);
if (links) {
songLinks[songId] = links;
cache[songId] = true;
successCount++;
// Save progress after each successful fetch
await saveSongLinksJSON(songLinks);
await saveCache(cache);
} else {
failCount++;
}
requestCount++;
}
}
// Save final TypeScript file
console.log("\n💾 Generating TypeScript file...");
await saveSongLinksTS(songLinks);
// Summary
console.log("\n" + "=".repeat(50));
console.log("📊 Summary:");
console.log(` Total requests: ${requestCount}`);
console.log(` Successful: ${successCount}`);
console.log(` Skipped: ${skipCount}`);
console.log(` Failed: ${failCount}`);
console.log("=".repeat(50));
// Format files with prettier
console.log("\n🎨 Formatting files with Prettier...");
try {
await $`bunx prettier --write ${OUTPUT_TS_FILE} ${OUTPUT_JSON_FILE} ${CACHE_FILE}`;
console.log(` ✓ Formatted all files`);
} catch (error) {
console.error(" ⚠️ Prettier formatting failed:", error);
}
console.log("\n✨ Done!");
console.log(`\n📁 Output files:`);
console.log(` - ${OUTPUT_TS_FILE}`);
console.log(` - ${OUTPUT_JSON_FILE}`);
console.log(` - ${CACHE_FILE}`);
}
// Run the script
main().catch((error) => {
console.error("❌ Fatal error:", error);
process.exit(1);
});

View file

@ -39,7 +39,7 @@ export default async function AlbumDetail({ params }: { params: Promise<{ albumI
const theme = getThemeColors(albumId); const theme = getThemeColors(albumId);
return ( return (
<Box className="min-h-screen bg-gray-900"> <Box className="min-h-screen">
<Container size="xl" className="py-12"> <Container size="xl" className="py-12">
{/* Back Button */} {/* Back Button */}
<BackButton /> <BackButton />
@ -76,69 +76,6 @@ export default async function AlbumDetail({ params }: { params: Promise<{ albumI
</GridCol> </GridCol>
</Grid> </Grid>
{/* Album Header */}
{/* <Box className="mb-12 grid grid-cols-1 gap-12 lg:grid-cols-3">
{/* Album Cover */}
{/* <Box className="lg:col-span-1">
<Card className="overflow-hidden border border-gray-700 bg-gray-800/50 backdrop-blur-sm">
<Image
src={album.image}
alt={album.label}
width={250}
height={250}
className="transition-all duration-700 ease-out"
/>
</Card>
</Box> */}
{/* Album Info */}
{/* <Box className="space-y-6 lg:col-span-2">
<Stack gap="md">
<Title order={1} className="text-6xl font-bold text-white">
{album.label}
</Title>
<Text size="xl" className="text-gray-400">
Released:{" "}
{new Date(album.releaseDate).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
</Text>
<Group gap="md">
<Badge
size="lg"
style={{
backgroundColor: theme.primary,
color: "white",
}}
>
{album.tracks.length} Songs
</Badge>
<Badge
size="lg"
variant="light"
style={{
backgroundColor: `${theme.secondary}20`,
color: theme.secondary,
borderColor: theme.secondary,
}}
>
Linkin Park
</Badge>
</Group>
</Stack>
<Divider color={theme.primary} />
<Text size="lg" className="leading-relaxed text-gray-300">
{album.description}
</Text>
</Box> */}
{/* </Box> */}
{/* Track List */} {/* Track List */}
{album.tracks.length > 0 && ( {album.tracks.length > 0 && (
<Box> <Box>

View file

@ -5,6 +5,9 @@ export type Track = {
studioUrl: string | null; studioUrl: string | null;
emilyLiveUrl: string | null; emilyLiveUrl: string | null;
lpLiveUrl: string | null; lpLiveUrl: string | null;
// this is the spotify url for the album
__SPOTIFY_URL__: string;
}; };
export type Album = { export type Album = {

View file

@ -11,100 +11,124 @@ export const albums: Album[] = [
"Hybrid Theory is the debut studio album by American rock band Linkin Park, released on October 24, 2000, by Warner Bros. Records. It was recorded at The Plant Studios in Sausalito, California, and produced by Don Gilmore. The album was a commercial success, reaching number one on the Billboard 200 chart and selling over 10 million copies in the United States alone.", "Hybrid Theory is the debut studio album by American rock band Linkin Park, released on October 24, 2000, by Warner Bros. Records. It was recorded at The Plant Studios in Sausalito, California, and produced by Don Gilmore. The album was a commercial success, reaching number one on the Billboard 200 chart and selling over 10 million copies in the United States alone.",
tracks: [ tracks: [
{ {
id: "1", id: "hybrid-theory-1",
label: "Papercut", label: "Papercut",
duration: "03:04", duration: "03:04",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/4HvcbAR8LisalXKt7cpqE1?si=a736ed41b9e84ff9",
}, },
{ {
id: "2", id: "hybrid-theory-2",
label: "One Step Closer", label: "One Step Closer",
duration: "02:35", duration: "02:35",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/4bYLTrlcqctyHck3fjhMgW?si=dd2c47138fed4c4c",
}, },
{ {
id: "3", id: "hybrid-theory-3",
label: "With You", label: "With You",
duration: "03:23", duration: "03:23",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/1Q9QN6dHEDeUegsySY012f?si=d4a1aca393954caa",
}, },
{ {
id: "4", id: "hybrid-theory-4",
label: "Points of Authority", label: "Points of Authority",
duration: "03:20", duration: "03:20",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/214LJVTqrkUOGemWDGHJpr?si=749f11d7cd9a4f56",
}, },
{ {
id: "5", id: "hybrid-theory-5",
label: "Crawling", label: "Crawling",
duration: "03:29", duration: "03:29",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/0X576XiCW9uEtko6f6VkcU?si=0a47dc1d660b4c71",
}, },
{ {
id: "6", id: "hybrid-theory-6",
label: "Runaway", label: "Runaway",
duration: "03:03", duration: "03:03",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/5tkHje8FGSF258gi869GcV?si=f69a7e1ebf0f4f31",
}, },
{ {
id: "7", id: "hybrid-theory-7",
label: "By Myself", label: "By Myself",
duration: "03:09", duration: "03:09",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/4SJoEhpI6C61wtUfxSHMGc?si=4ca78fd74ae74d6f",
}, },
{ {
id: "8", id: "hybrid-theory-8",
label: "In the End", label: "In the End",
duration: "03:36", duration: "03:36",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/7q115ia4fQn9zonjpexWsY?si=1e4cb0fe76244b11",
}, },
{ {
id: "9", id: "hybrid-theory-9",
label: "A Place for My Head", label: "A Place for My Head",
duration: "03:04", duration: "03:04",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/1B8WdDScvobpFsZLfdmIE1?si=f1b5b6fe0abb4e23",
}, },
{ {
id: "10", id: "hybrid-theory-10",
label: "Forgotten", label: "Forgotten",
duration: "03:04", duration: "03:04",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/3JAu2BH8sSI46rJGzQerb6?si=92186e546f0049b9",
}, },
{ {
id: "11", id: "hybrid-theory-11",
label: "Cure for the Itch", label: "Cure for the Itch",
duration: "02:37", duration: "02:37",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/15voDM9pGkwEvHBUecpcmo?si=0b50fb655a2e4649",
}, },
{ {
id: "12", id: "hybrid-theory-12",
label: "Pushing Me Away", label: "Pushing Me Away",
duration: "03:11", duration: "03:11",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/4HlMOXxwF9SYG1RoJxEjio?si=1b9286e4d2c947d3",
}, },
], ],
}, },
@ -118,216 +142,242 @@ export const albums: Album[] = [
"Meteora is the second studio album by American rock band Linkin Park, released on March 24, 2003, by Warner Bros. Records. It was recorded at The Plant Studios in Sausalito, California, and produced by Don Gilmore. The album was a commercial success, reaching number one on the Billboard 200 chart and selling over 10 million copies in the United States alone.", "Meteora is the second studio album by American rock band Linkin Park, released on March 24, 2003, by Warner Bros. Records. It was recorded at The Plant Studios in Sausalito, California, and produced by Don Gilmore. The album was a commercial success, reaching number one on the Billboard 200 chart and selling over 10 million copies in the United States alone.",
tracks: [ tracks: [
{ {
id: "1", id: "meteora-1",
label: "Foreword", label: "Foreword",
duration: "00:13", duration: "00:13",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/4hbpxoMCp62bd1sdy7XehS?si=0c4b2f93b36e43e7",
}, },
{ {
id: "2", id: "meteora-2",
label: "Don't Stay", label: "Don't Stay",
duration: "03:07", duration: "03:07",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/6HZdbb05lEXLvcmee3ZXO2?si=b1f8362c72694a1b",
}, },
{ {
id: "3", id: "meteora-3",
label: "Somewhere I Belong", label: "Somewhere I Belong",
duration: "03:33", duration: "03:33",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/3agtg0x11wPvLIWkYR39nZ?si=e79dfe0143374cd9",
}, },
{ {
id: "4", id: "meteora-4",
label: "Lying from You", label: "Lying from You",
duration: "02:55", duration: "02:55",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/4zP2e2aIzOZGEFTq1MDJmm?si=a7951a3a01e740ac",
}, },
{ {
id: "5", id: "meteora-5",
label: "Hit the Floor", label: "Hit the Floor",
duration: "02:44", duration: "02:44",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/3R9NKZ4jmxNjOFihSF5s7f?si=3f07863add1e4e0f",
}, },
{ {
id: "6", id: "meteora-6",
label: "Easier to Run", label: "Easier to Run",
duration: "03:24", duration: "03:24",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/17bgialGAwoiGj1STY4cnR?si=679a5b245d1d4610",
}, },
{ {
id: "7", id: "meteora-7",
label: "Faint", label: "Faint",
duration: "02:42", duration: "02:42",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/4Yf5bqU3NK4kNOypcrLYwU?si=cf2f7362cff04704",
}, },
{ {
id: "8", id: "meteora-8",
label: "Figure.09", label: "Figure.09",
duration: "03:17", duration: "03:17",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/2DrMcGNKEY0FZqjMovGaCm?si=583eb32931e84c48",
}, },
{ {
id: "9", id: "meteora-9",
label: "Breaking the Habit", label: "Breaking the Habit",
duration: "03:16", duration: "03:16",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/6n8TMVyFKoUmDc4apxceRD?si=2b17f6b1906f46ec",
}, },
{ {
id: "10", id: "meteora-10",
label: "From the Inside", label: "From the Inside",
duration: "02:55", duration: "02:55",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/60IkVf7UfQXmt5CwkpcX8a?si=56ad75e64c174188",
}, },
{ {
id: "11", id: "meteora-11",
label: "Nobody's Listening", label: "Nobody's Listening",
duration: "02:58", duration: "02:58",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/4m1P1BSg9nLxaWbvPhNdhw?si=9671f8d0299f429f",
}, },
{ {
id: "12", id: "meteora-12",
label: "Session", label: "Session",
duration: "02:24", duration: "02:24",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/3sbezh9MMCQrr4KY7zw13j?si=62626db46f734d63",
}, },
{ {
id: "13", id: "meteora-13",
label: "Numb", label: "Numb",
duration: "03:05", duration: "03:05",
studioUrl: null, studioUrl: null,
emilyLiveUrl: null, emilyLiveUrl: null,
lpLiveUrl: null, lpLiveUrl: null,
__SPOTIFY_URL__: "https://open.spotify.com/track/2nLtzopw4rPReszdYBJU6h?si=8023141252ac4fec",
}, },
], ],
}, },
{ // {
id: "minutes-to-midnight", // id: "minutes-to-midnight",
label: "Minutes to Midnight", // label: "Minutes to Midnight",
releaseDate: "2007-05-14", // releaseDate: "2007-05-14",
image: "/minutes_to_midnight.jpg", // image: "/minutes_to_midnight.jpg",
url: "/minutes-to-midnight", // url: "/minutes-to-midnight",
description: // description:
"Minutes to Midnight is the third studio album by American rock band Linkin Park, released on May 14, 2007, by Warner Bros. Records. The album marked a departure from the band's previous nu-metal sound, incorporating more alternative rock and experimental elements.", // "Minutes to Midnight is the third studio album by American rock band Linkin Park, released on May 14, 2007, by Warner Bros. Records. The album marked a departure from the band's previous nu-metal sound, incorporating more alternative rock and experimental elements.",
tracks: [ // tracks: [
{ // {
id: "1", // id: "minutes-to-midnight-1",
label: "Wake", // label: "Wake",
duration: "01:40", // duration: "01:40",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "2", // id: "minutes-to-midnight-2",
label: "Given Up", // label: "Given Up",
duration: "03:09", // duration: "03:09",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "3", // id: "minutes-to-midnight-3",
label: "Leave Out All the Rest", // label: "Leave Out All the Rest",
duration: "03:29", // duration: "03:29",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "4", // id: "minutes-to-midnight-4",
label: "Bleed It Out", // label: "Bleed It Out",
duration: "02:44", // duration: "02:44",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "5", // id: "minutes-to-midnight-5",
label: "Shadow of the Day", // label: "Shadow of the Day",
duration: "04:49", // duration: "04:49",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "6", // id: "minutes-to-midnight-6",
label: "What I've Done", // label: "What I've Done",
duration: "03:25", // duration: "03:25",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "7", // id: "minutes-to-midnight-7",
label: "Hands Held High", // label: "Hands Held High",
duration: "03:53", // duration: "03:53",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "8", // id: "minutes-to-midnight-8",
label: "No More Sorrow", // label: "No More Sorrow",
duration: "03:41", // duration: "03:41",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "9", // id: "minutes-to-midnight-9",
label: "Valentine's Day", // label: "Valentine's Day",
duration: "03:16", // duration: "03:16",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "10", // id: "minutes-to-midnight-10",
label: "In Between", // label: "In Between",
duration: "03:16", // duration: "03:16",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "11", // id: "minutes-to-midnight-11",
label: "In Pieces", // label: "In Pieces",
duration: "03:38", // duration: "03:38",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
{ // {
id: "12", // id: "minutes-to-midnight-12",
label: "The Little Things Give You Away", // label: "The Little Things Give You Away",
duration: "06:23", // duration: "06:23",
studioUrl: null, // studioUrl: null,
emilyLiveUrl: null, // emilyLiveUrl: null,
lpLiveUrl: null, // lpLiveUrl: null,
}, // },
], // ],
}, // },
]; ];

193
src/lib/songLinks.ts Normal file
View file

@ -0,0 +1,193 @@
// This file is auto-generated by scripts/fetch-song-links.ts
// Do not edit manually
// Last updated: 2025-10-24T22:59:54.578Z
// Run: bun run fetch-links
export interface SongLinks {
spotify?: string;
youtube?: string;
youtubeMusic?: string;
appleMusic?: string;
}
export interface SongLinksData {
[songId: string]: SongLinks;
}
export const songLinks: SongLinksData = {
"hybrid-theory-1": {
spotify: "https://open.spotify.com/track/4HvcbAR8LisalXKt7cpqE1",
youtube: "https://www.youtube.com/watch?v=vjVkXlxsO8Q",
youtubeMusic: "https://music.youtube.com/watch?v=vjVkXlxsO8Q",
appleMusic:
"https://geo.music.apple.com/de/album/_/528436018?i=528436568&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-2": {
spotify: "https://open.spotify.com/track/4bYLTrlcqctyHck3fjhMgW",
youtube: "https://www.youtube.com/watch?v=4qlCC1GOwFw",
youtubeMusic: "https://music.youtube.com/watch?v=4qlCC1GOwFw",
appleMusic:
"https://geo.music.apple.com/de/album/_/590431776?i=590431778&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-3": {
spotify: "https://open.spotify.com/track/1Q9QN6dHEDeUegsySY012f",
youtube: "https://www.youtube.com/watch?v=M8UTS2iFXOo",
youtubeMusic: "https://music.youtube.com/watch?v=M8UTS2iFXOo",
appleMusic:
"https://geo.music.apple.com/de/album/_/528436018?i=528437421&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-4": {
spotify: "https://open.spotify.com/track/214LJVTqrkUOGemWDGHJpr",
youtube: "https://www.youtube.com/watch?v=jZSPAp8kCl4",
youtubeMusic: "https://music.youtube.com/watch?v=jZSPAp8kCl4",
appleMusic:
"https://geo.music.apple.com/de/album/_/590431776?i=590431781&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-5": {
spotify: "https://open.spotify.com/track/0X576XiCW9uEtko6f6VkcU",
youtube: "https://www.youtube.com/watch?v=Gd9OhYroLN0",
youtubeMusic: "https://music.youtube.com/watch?v=Gd9OhYroLN0",
appleMusic:
"https://geo.music.apple.com/de/album/_/591534774?i=591534780&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-6": {
spotify: "https://open.spotify.com/track/5tkHje8FGSF258gi869GcV",
youtube: "https://www.youtube.com/watch?v=ig-fyQqf510",
youtubeMusic: "https://music.youtube.com/watch?v=ig-fyQqf510",
appleMusic:
"https://geo.music.apple.com/de/album/_/528436018?i=528437609&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-7": {
spotify: "https://open.spotify.com/track/4SJoEhpI6C61wtUfxSHMGc",
youtube: "https://www.youtube.com/watch?v=wWBp-nlGX1o",
youtubeMusic: "https://music.youtube.com/watch?v=wWBp-nlGX1o",
appleMusic:
"https://geo.music.apple.com/de/album/_/528436018?i=528437611&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-8": {
spotify: "https://open.spotify.com/track/7q115ia4fQn9zonjpexWsY",
youtube: "https://www.youtube.com/watch?v=eVTXPUF4Oz4",
youtubeMusic: "https://music.youtube.com/watch?v=eVTXPUF4Oz4",
appleMusic:
"https://geo.music.apple.com/de/album/_/528436018?i=528437613&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-9": {
spotify: "https://open.spotify.com/track/1B8WdDScvobpFsZLfdmIE1",
youtube: "https://www.youtube.com/watch?v=3t2WkCudwfY",
youtubeMusic: "https://music.youtube.com/watch?v=3t2WkCudwfY",
appleMusic:
"https://geo.music.apple.com/de/album/_/528436018?i=528437708&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-10": {
spotify: "https://open.spotify.com/track/3JAu2BH8sSI46rJGzQerb6",
youtube: "https://www.youtube.com/watch?v=HNCgBuI2eJc",
youtubeMusic: "https://music.youtube.com/watch?v=HNCgBuI2eJc",
appleMusic:
"https://geo.music.apple.com/de/album/_/528436018?i=528437709&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-11": {
spotify: "https://open.spotify.com/track/15voDM9pGkwEvHBUecpcmo",
youtube: "https://www.youtube.com/watch?v=qqC5sdsHLq8",
youtubeMusic: "https://music.youtube.com/watch?v=qqC5sdsHLq8",
appleMusic:
"https://geo.music.apple.com/de/album/_/528436018?i=528437710&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"hybrid-theory-12": {
spotify: "https://open.spotify.com/track/4HlMOXxwF9SYG1RoJxEjio",
youtube: "https://www.youtube.com/watch?v=Ve1LNJEIKUE",
youtubeMusic: "https://music.youtube.com/watch?v=Ve1LNJEIKUE",
appleMusic:
"https://geo.music.apple.com/de/album/_/590431776?i=590431790&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-1": {
spotify: "https://open.spotify.com/track/4hbpxoMCp62bd1sdy7XehS",
youtube: "https://www.youtube.com/watch?v=U6R-twDkrcI",
youtubeMusic: "https://music.youtube.com/watch?v=U6R-twDkrcI",
appleMusic:
"https://geo.music.apple.com/de/album/_/590423275?i=590423280&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-2": {
spotify: "https://open.spotify.com/track/6HZdbb05lEXLvcmee3ZXO2",
youtube: "https://www.youtube.com/watch?v=oWfGOVWrueo",
youtubeMusic: "https://music.youtube.com/watch?v=oWfGOVWrueo",
appleMusic:
"https://geo.music.apple.com/de/album/_/1668484895?i=1668485277&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-3": {
spotify: "https://open.spotify.com/track/3agtg0x11wPvLIWkYR39nZ",
youtube: "https://www.youtube.com/watch?v=zsCD5XCu6CM",
youtubeMusic: "https://music.youtube.com/watch?v=zsCD5XCu6CM",
appleMusic:
"https://geo.music.apple.com/de/album/_/528435845?i=528437020&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-4": {
spotify: "https://open.spotify.com/track/4zP2e2aIzOZGEFTq1MDJmm",
youtube: "https://www.youtube.com/watch?v=NjdgcHdzvac",
youtubeMusic: "https://music.youtube.com/watch?v=NjdgcHdzvac",
appleMusic:
"https://geo.music.apple.com/de/album/_/590423275?i=590423283&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-5": {
spotify: "https://open.spotify.com/track/3R9NKZ4jmxNjOFihSF5s7f",
youtube: "https://www.youtube.com/watch?v=oMals9XXQY8",
youtubeMusic: "https://music.youtube.com/watch?v=oMals9XXQY8",
appleMusic:
"https://geo.music.apple.com/de/album/_/590423275?i=590423284&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-6": {
spotify: "https://open.spotify.com/track/17bgialGAwoiGj1STY4cnR",
youtube: "https://www.youtube.com/watch?v=U5zdmjVeQzE",
youtubeMusic: "https://music.youtube.com/watch?v=U5zdmjVeQzE",
appleMusic:
"https://geo.music.apple.com/de/album/_/590423275?i=590423285&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-7": {
spotify: "https://open.spotify.com/track/4Yf5bqU3NK4kNOypcrLYwU",
youtube: "https://www.youtube.com/watch?v=LYU-8IFcDPw",
youtubeMusic: "https://music.youtube.com/watch?v=LYU-8IFcDPw",
appleMusic:
"https://geo.music.apple.com/de/album/_/528435845?i=528437024&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-8": {
spotify: "https://open.spotify.com/track/2DrMcGNKEY0FZqjMovGaCm",
youtube: "https://www.youtube.com/watch?v=6dEAeCHQrBs",
youtubeMusic: "https://music.youtube.com/watch?v=6dEAeCHQrBs",
appleMusic:
"https://geo.music.apple.com/de/album/_/528435845?i=528437025&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-9": {
spotify: "https://open.spotify.com/track/6n8TMVyFKoUmDc4apxceRD",
youtube: "https://www.youtube.com/watch?v=v2H4l9RpkwM",
youtubeMusic: "https://music.youtube.com/watch?v=v2H4l9RpkwM",
appleMusic:
"https://geo.music.apple.com/de/album/_/528435845?i=528437026&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-10": {
spotify: "https://open.spotify.com/track/60IkVf7UfQXmt5CwkpcX8a",
youtube: "https://www.youtube.com/watch?v=YLHpvjrFpe0",
youtubeMusic: "https://music.youtube.com/watch?v=YLHpvjrFpe0",
appleMusic:
"https://geo.music.apple.com/de/album/_/528435845?i=528437027&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-11": {
spotify: "https://open.spotify.com/track/4m1P1BSg9nLxaWbvPhNdhw",
youtube: "https://www.youtube.com/watch?v=QJ87793QXes",
youtubeMusic: "https://music.youtube.com/watch?v=QJ87793QXes",
appleMusic:
"https://geo.music.apple.com/de/album/_/590423275?i=590423550&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-12": {
spotify: "https://open.spotify.com/track/3sbezh9MMCQrr4KY7zw13j",
youtube: "https://www.youtube.com/watch?v=J1KqQYsUYIk",
youtubeMusic: "https://music.youtube.com/watch?v=J1KqQYsUYIk",
appleMusic:
"https://geo.music.apple.com/de/album/_/528435845?i=528437513&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
"meteora-13": {
spotify: "https://open.spotify.com/track/2nLtzopw4rPReszdYBJU6h",
youtube: "https://www.youtube.com/watch?v=kXYiU_JCYtU",
youtubeMusic: "https://music.youtube.com/watch?v=kXYiU_JCYtU",
appleMusic:
"https://geo.music.apple.com/de/album/_/528435845?i=528437514&mt=1&app=music&ls=1&at=1000lHKX&ct=api_http&itscg=30200&itsct=odsl_m",
},
};

View file

@ -2,6 +2,7 @@
"compilerOptions": { "compilerOptions": {
"target": "ES2020", "target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"], "lib": ["ES2020", "DOM", "DOM.Iterable"],
"types": ["bun-types"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
"moduleResolution": "bundler", "moduleResolution": "bundler",