melanie hat die Gist bearbeitet 8 months ago. Zu Änderung gehen
Keine Änderungen
melanie hat die Gist bearbeitet 8 months ago. Zu Änderung gehen
Keine Änderungen
melanie hat die Gist bearbeitet 11 months ago. Zu Änderung gehen
Keine Änderungen
melanie hat die Gist bearbeitet 11 months ago. Zu Änderung gehen
1 file changed, 131 insertions
lastfm-weekly-report.ts(Datei erstellt)
| @@ -0,0 +1,131 @@ | |||
| 1 | + | const LASTFM_API_KEY = process.env.FEED_LASTFM_API_KEY || ""; | |
| 2 | + | const LASTFM_USERNAME = process.env.FEED_LASTFM_USERNAME || ""; | |
| 3 | + | const LASTFM_API = "https://ws.audioscrobbler.com/2.0/"; | |
| 4 | + | ||
| 5 | + | function formatItem({ | |
| 6 | + | artist, | |
| 7 | + | image, | |
| 8 | + | name, | |
| 9 | + | playcount, | |
| 10 | + | url, | |
| 11 | + | }: { | |
| 12 | + | artist: { "#text": string }; | |
| 13 | + | image?: { "#text": string }[]; | |
| 14 | + | name: string; | |
| 15 | + | playcount: string; | |
| 16 | + | url: string; | |
| 17 | + | }) { | |
| 18 | + | return { | |
| 19 | + | name: artist ? `${name} by ${artist?.["#text"]}` : name, | |
| 20 | + | image: image ? image?.at(-1)?.["#text"] : undefined, | |
| 21 | + | playcount, | |
| 22 | + | url, | |
| 23 | + | }; | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | function epochToIso(epoch: string) { | |
| 27 | + | const date = new Date(Number.parseInt(epoch) * 1000); | |
| 28 | + | return date?.toISOString(); | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | function isoToYearAndWeek(iso: string) { | |
| 32 | + | const date = new Date(iso); | |
| 33 | + | const year = date.getFullYear(); | |
| 34 | + | const firstOfYear = new Date(year, 0, 1); | |
| 35 | + | const daysSinceFirstOfYear = Math.floor( | |
| 36 | + | (+date - +firstOfYear) / (24 * 60 * 60 * 1000), | |
| 37 | + | ); | |
| 38 | + | const week = Math.ceil((date.getDay() + 1 + daysSinceFirstOfYear) / 7); | |
| 39 | + | return { | |
| 40 | + | week, | |
| 41 | + | year, | |
| 42 | + | }; | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | function listToHTML( | |
| 46 | + | arr: { | |
| 47 | + | name: string; | |
| 48 | + | playcount: string; | |
| 49 | + | url: string; | |
| 50 | + | }[], | |
| 51 | + | ) { | |
| 52 | + | return arr | |
| 53 | + | .map( | |
| 54 | + | ({ name, playcount, url }) => | |
| 55 | + | `<li><a href="${url}">${name}</a> (${playcount} plays)</li>`, | |
| 56 | + | ) | |
| 57 | + | .join(""); | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | async function lastFm(method = "") { | |
| 61 | + | const data = await fetch( | |
| 62 | + | `${LASTFM_API}?method=${method}&api_key=${LASTFM_API_KEY}&format=json&user=${LASTFM_USERNAME}`, | |
| 63 | + | ); | |
| 64 | + | return await data.json(); | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | async function getWeeklyChartList(limit = 5) { | |
| 68 | + | const data = await lastFm("user.getWeeklyChartList"); | |
| 69 | + | return data?.weeklychartlist?.chart?.slice(limit * -1); | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | async function getWeeklyTrackChart(to: string, from: string, limit = 5) { | |
| 73 | + | const data = await lastFm(`user.getWeeklyTrackChart&from=${from}&to=${to}`); | |
| 74 | + | return data?.weeklytrackchart?.track?.slice(0, limit)?.map(formatItem); | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | async function getWeeklyAlbumChart(to: string, from: string, limit = 5) { | |
| 78 | + | const data = await lastFm(`user.getWeeklyAlbumChart&from=${from}&to=${to}`); | |
| 79 | + | return data?.weeklyalbumchart?.album?.slice(0, limit)?.map(formatItem); | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | async function getWeeklyArtistChart(to: string, from: string, limit = 5) { | |
| 83 | + | const data = await lastFm(`user.getWeeklyArtistChart&from=${from}&to=${to}`); | |
| 84 | + | return data?.weeklyartistchart?.artist?.slice(0, limit)?.map(formatItem); | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | async function buildReport() { | |
| 88 | + | const output = []; | |
| 89 | + | const weeks = await getWeeklyChartList(); | |
| 90 | + | for (const week of weeks) { | |
| 91 | + | output.push({ | |
| 92 | + | to: week.to, | |
| 93 | + | from: week.from, | |
| 94 | + | tracks: await getWeeklyTrackChart(week.to, week.from), | |
| 95 | + | albums: await getWeeklyAlbumChart(week.to, week.from), | |
| 96 | + | artists: await getWeeklyArtistChart(week.to, week.from), | |
| 97 | + | }); | |
| 98 | + | } | |
| 99 | + | return output; | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | export default async function buildFeedItems() { | |
| 103 | + | const data = await buildReport(); | |
| 104 | + | return data | |
| 105 | + | .map(({ to, from, tracks, albums, artists }) => { | |
| 106 | + | const trackList = listToHTML(tracks); | |
| 107 | + | const albumList = listToHTML(albums); | |
| 108 | + | const artistList = listToHTML(artists); | |
| 109 | + | const toIso = epochToIso(to); | |
| 110 | + | const fromIso = epochToIso(from); | |
| 111 | + | const { week, year } = isoToYearAndWeek(fromIso); | |
| 112 | + | ||
| 113 | + | if (!trackList && !albumList && !artistList) { | |
| 114 | + | return; | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | return { | |
| 118 | + | content_html: [ | |
| 119 | + | artistList ? `<h2>Top Artists</h2><ol>${artistList}</ol>` : "", | |
| 120 | + | albumList ? `<h2>Top Albums</h2><ol>${albumList}</ol>` : "", | |
| 121 | + | trackList ? `<h2>Top Tracks</h2><ol>${trackList}</ol>` : "", | |
| 122 | + | ].join(""), | |
| 123 | + | date_published: toIso, | |
| 124 | + | id: `last.fm-${from}-${to}`, | |
| 125 | + | title: "weekly last.fm report", | |
| 126 | + | url: `https://www.last.fm/user/ZicklePop/listening-report/year/${year}/week/${week}`, | |
| 127 | + | tags: ["last.fm"], | |
| 128 | + | }; | |
| 129 | + | }) | |
| 130 | + | .filter((o) => !!o); | |
| 131 | + | } | |
Neuer
Älter