mirror of
https://git.stupid.fish/teidesu/scripts.git
synced 2025-10-12 07:41:23 +11:00
chore: update public repo
This commit is contained in:
parent
96ca247fcb
commit
261c7eefa0
1 changed files with 223 additions and 0 deletions
223
scripts/misc/spotify-albums-weekday-stats.ts
Normal file
223
scripts/misc/spotify-albums-weekday-stats.ts
Normal 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()
|
Loading…
Add table
Add a link
Reference in a new issue