chore: update public repo

This commit is contained in:
desu-bot 2025-09-21 19:26:47 +00:00
parent 96ca247fcb
commit 261c7eefa0
No known key found for this signature in database

View file

@ -0,0 +1,223 @@
#!/usr/bin/env tsx
import { ffetch } from '../../utils/fetch.ts'
import { getEnv } from '../../utils/misc.ts'
// context: had a discussion in a group chat about which day of the week albums are usually released on, needed a way to find out
// the script is mostly vibe-coded but i have no intentions to run it more than once so who cares
interface SpotifyTrack {
track: {
id: string
name: string
album: {
id: string
name: string
release_date: string
release_date_precision: 'year' | 'month' | 'day'
}
artists: Array<{
name: string
}>
}
}
interface SpotifyAlbum {
id: string
name: string
release_date: string
release_date_precision: 'year' | 'month' | 'day'
artists: Array<{
name: string
}>
}
interface SpotifyResponse<T> {
items: T[]
next: string | null
total: number
}
class SpotifyClient {
private accessToken: string
private baseUrl = 'https://api.spotify.com/v1'
constructor(accessToken: string) {
this.accessToken = accessToken
}
private async makeRequest<T>(endpoint: string): Promise<T> {
const response = await ffetch(endpoint, {
baseUrl: this.baseUrl,
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
},
})
if (!response.ok) {
throw new Error(`Spotify API error: ${response.status} ${response.statusText}: ${await response.text()}`)
}
return response.json()
}
async getLikedTracks(): Promise<SpotifyTrack[]> {
const allTracks: SpotifyTrack[] = []
let url = '/me/tracks?limit=50'
while (url) {
const response = await this.makeRequest<SpotifyResponse<SpotifyTrack>>(url)
allTracks.push(...response.items)
console.log(`Fetched ${allTracks.length} out of ${response.total} tracks`)
url = response.next ? response.next.replace(this.baseUrl, '') : ''
}
return allTracks
}
async getAlbum(albumId: string): Promise<SpotifyAlbum> {
return this.makeRequest<SpotifyAlbum>(`/albums/${albumId}`)
}
}
interface DayStats {
[key: string]: {
count: number
albums: Array<{
name: string
artist: string
releaseDate: string
}>
}
}
function getDayOfWeek(dateString: string, precision: string): string {
if (precision === 'year') {
return 'Unknown (Year only)'
}
if (precision === 'month') {
return 'Unknown (Month only)'
}
try {
const date = new Date(dateString)
if (Number.isNaN(date.getTime())) {
return 'Unknown (Invalid date)'
}
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
return days[date.getDay()]
} catch (error) {
return 'Unknown (Parse error)'
}
}
async function main() {
const accessToken = getEnv('SPOTIFY_API_TOKEN')
if (!accessToken) {
console.error('Error: SPOTIFY_API_TOKEN environment variable is required')
process.exit(1)
}
console.log('🎵 Fetching your liked tracks from Spotify...')
const spotify = new SpotifyClient(accessToken)
try {
const likedTracks = await spotify.getLikedTracks()
console.log(`Found ${likedTracks.length} liked tracks`)
const processedAlbums = new Set<string>()
const dayStats: DayStats = {}
// First, count unique albums from tracks
const uniqueAlbumIds = new Set<string>()
for (const track of likedTracks) {
uniqueAlbumIds.add(track.track.album.id)
}
console.log(`📊 Analyzing ${uniqueAlbumIds.size} unique album release dates...`)
let processedCount = 0
let skippedCount = 0
for (const track of likedTracks) {
const albumId = track.track.album.id
// Skip if we've already processed this album
if (processedAlbums.has(albumId)) {
skippedCount++
continue
}
processedAlbums.add(albumId)
processedCount++
try {
// Get detailed album info
const album = await spotify.getAlbum(albumId)
const dayOfWeek = getDayOfWeek(album.release_date, album.release_date_precision)
if (!dayStats[dayOfWeek]) {
dayStats[dayOfWeek] = {
count: 0,
albums: [],
}
}
dayStats[dayOfWeek].count++
dayStats[dayOfWeek].albums.push({
name: album.name,
artist: album.artists.map(a => a.name).join(', '),
releaseDate: album.release_date,
})
// Progress reporting
if (processedCount % 10 === 0 || processedCount === uniqueAlbumIds.size) {
console.log(`Progress: ${processedCount}/${uniqueAlbumIds.size} albums processed (${skippedCount} skipped)`)
}
// Add a small delay to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 100))
} catch (error) {
console.warn(`Failed to fetch album info for ${track.track.album.name}: ${error}`)
}
}
console.log('\n📈 Album Release Day Statistics')
console.log('='.repeat(50))
// Sort by count (descending)
const sortedStats = Object.entries(dayStats)
.sort(([,a], [,b]) => b.count - a.count)
for (const [day, stats] of sortedStats) {
console.log(`\n${day}: ${stats.count} albums`)
console.log('-'.repeat(30))
// Show top 5 albums for this day
const topAlbums = stats.albums.slice(0, 5)
for (const album of topAlbums) {
console.log(`${album.name} by ${album.artist} (${album.releaseDate})`)
}
if (stats.albums.length > 5) {
console.log(` ... and ${stats.albums.length - 5} more`)
}
}
console.log('\n📊 Summary:')
console.log(`Total unique albums found: ${uniqueAlbumIds.size}`)
console.log(`Total unique albums analyzed: ${processedAlbums.size}`)
console.log(`Albums skipped (duplicates): ${skippedCount}`)
console.log(`Total liked tracks: ${likedTracks.length}`)
} catch (error) {
console.error('Error:', error)
process.exit(1)
}
}
await main()