最終更新 1741105227

melanie revised this gist 1741105227. Go to revision

No changes

melanie revised this gist 1741105203. Go to revision

No changes

melanie revised this gist 1732922313. Go to revision

No changes

melanie revised this gist 1732918400. Go to revision

1 file changed, 131 insertions

lastfm-weekly-report.ts(file created)

@@ -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 + }
Newer Older