Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: generate one page per episode #3

Merged
merged 6 commits into from Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions astro.config.mjs
@@ -1,9 +1,9 @@
import { defineConfig } from 'astro/config';
import { defineConfig } from "astro/config";

import netlify from "@astrojs/netlify";

// https://astro.build/config
export default defineConfig({
output: "server",
adapter: netlify()
});
output: "static",
adapter: netlify(),
});
12 changes: 11 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"@astrojs/netlify": "^5.1.3",
"astro": "^4.4.10"
"astro": "^4.4.10",
"date-fns": "^3.3.1"
}
}
123 changes: 123 additions & 0 deletions src/data/index.ts
@@ -0,0 +1,123 @@
import { format, sub } from "date-fns";

interface Episode {
slug: string;
title: string;
description: string;
embed_html: string;
}

interface EpisodeData {
id: string;
type: string;
attributes: Episode;
}

interface TransistorEpisodeResponse {
data: EpisodeData[];
meta: {
totalCount: number;
totalPages: number;
currentPage: number;
};
}

const TRANSISTOR_API_KEY = import.meta.env.TRANSISTOR_API_KEY;
const TRANSISTOR_SHOW_ID = import.meta.env.TRANSISTOR_SHOW_ID;
const TODAY = new Date();
const START_DATE = sub(TODAY, { weeks: 53 });
const END_DATE = sub(TODAY, { weeks: 1 });

export async function fetchEpisodes(
apiKey = TRANSISTOR_API_KEY,
showId = TRANSISTOR_SHOW_ID,
status = "published",
pageSize = 50
) {
let meta = {
currentPage: 1,
totalPages: -1,
};

const episodeData: EpisodeData[] = [];

do {
const response: TransistorEpisodeResponse = await fetch(
`https://api.transistor.fm/v1/episodes?show_id=${showId}&status=${status}&pagination[per]=${pageSize}&pagination[page]=${meta.currentPage}`,
{
method: "GET",
headers: {
"x-api-key": apiKey,
},
}
).then((response) => response.json());

episodeData.push(...response.data);

meta = {
currentPage: response.meta.currentPage + 1,
totalPages: response.meta.totalPages,
};
} while (meta.currentPage <= meta.totalPages);

console.log("Fetched", episodeData.length, "episodes");

return episodeData;
}

export async function fetchEpisode(episodeId, apiKey = TRANSISTOR_API_KEY) {
const response = await fetch(
`https://api.transistor.fm/v1/episodes/${episodeId}`,
{
method: "GET",
headers: {
"x-api-key": apiKey,
},
}
).then((response) => response.json());
return response.data;
}

export async function fetchEpisodeAnalytics(
apiKey = TRANSISTOR_API_KEY,
showId = TRANSISTOR_SHOW_ID,
startDate = format(START_DATE, "dd-MM-yyyy"),
endDate = format(END_DATE, "dd-MM-yyyy")
) {
const response = await fetch(
`https://api.transistor.fm/v1/analytics/${showId}/episodes?start_date=${startDate}&end_date=${endDate}`,
{
method: "GET",
headers: {
"x-api-key": apiKey,
},
}
).then((response) => response.json());

console.log(
"FETCHING ANALYTICS",
response.data.attributes.episodes.length,
"episodes"
);

const analytics = response.data.attributes.episodes.map((episode: any) => {
episode.totalDownloads = episode.downloads.reduce(
(acc: number, day: any) => acc + day.downloads,
0
);
return episode;
});

return analytics;
}

export async function fetchMostPopuplarEpisodes(count = 5) {
const episodeAnalytics = await fetchEpisodeAnalytics();
const sortedEpisodes = episodeAnalytics.sort(
(a: any, b: any) => b.totalDownloads - a.totalDownloads
);
const mostPopular = sortedEpisodes.slice(0, count);
return await Promise.all(
mostPopular.map((episode: any) => fetchEpisode(episode.id))
);
}
9 changes: 9 additions & 0 deletions src/env.d.ts
@@ -1 +1,10 @@
/// <reference types="astro/client" />

interface ImportMetaEnv {
readonly TRANSISTOR_API_KEY: string;
readonly TRANSISTOR_SHOW_ID: string;
}

interface ImportMeta {
readonly env: ImportMetaEnv;
}
28 changes: 27 additions & 1 deletion src/layouts/Layout.astro
Expand Up @@ -18,6 +18,32 @@ const { title } = Astro.props;
<title>{title}</title>
</head>
<body>
<slot />
<header>
<nav>
<a href="/">Home</a>
<ul>
<li><a href="/episodes">All Episodes</a></li>
</ul>
</nav>
</header>
</body>
<slot />
</html>

<style is:global>
nav {
margin-bottom: 0;
}

article {
max-width: 60ch;
text-wrap: pretty;
margin-left: auto;
margin-right: auto;
}

header p {
margin-left: auto;
margin-right: auto;
}
</style>
46 changes: 46 additions & 0 deletions src/pages/episodes/[...page].astro
@@ -0,0 +1,46 @@
---
import { fetchEpisodes } from "../../data";
import Layout from "../../layouts/Layout.astro";

export async function getStaticPaths({ paginate }) {
const episodeData = await fetchEpisodes();

const episodes = episodeData.map((data) => {
return data.attributes;
});

const pages = paginate(episodes, { pageSize: 10 });

return pages;
}

const { page } = Astro.props;
---

<Layout title="Our podcast websites">
<main>
{
page.data.map((episode) => {
return (
<article>
<h2>
<a href={`/episodes/${episode.slug}`}>{episode.title}</a>
</h2>
<div set:html={episode.description} />
</article>
);
})
}

<nav>
<ul>
<li>
<a href={page.url.prev}>Previous</a>
</li>
<li>
<a href={page.url.next}>Next</a>
</li>
</ul>
</nav>
</main>
</Layout>
35 changes: 35 additions & 0 deletions src/pages/episodes/[slug].astro
@@ -0,0 +1,35 @@
---
import { fetchEpisodes } from "../../data";
import Layout from "../../layouts/Layout.astro";

export async function getStaticPaths() {
const episodeData = await fetchEpisodes();

const pages = episodeData.map((episode) => {
return {
params: { slug: episode.attributes.slug },
props: { episode: episode.attributes },
};
});

return pages;
}

const { episode } = Astro.props;
---

<Layout title="Our podcast websites">
<main>
<header>
<h1>{episode.title}</h1>
</header>

<div set:html={episode.embed_html} />

<hr />

<div set:html={episode.description} />

<hr />
</main>
</Layout>
38 changes: 25 additions & 13 deletions src/pages/index.astro
@@ -1,8 +1,14 @@
---
import { fetchMostPopuplarEpisodes } from "../data";
import Layout from "../layouts/Layout.astro";

const episodeData = await fetchMostPopuplarEpisodes();
const episodes = episodeData.map((data) => {
return data.attributes;
});
---

<Layout title="Our podcast websites">
<Layout title="Our podcast website">
<main>
<header>
<h1>Podcast Websites in Astro</h1>
Expand Down Expand Up @@ -39,17 +45,23 @@ import Layout from "../layouts/Layout.astro";
</p>
</aside>
</section>
<hr />
<section>
<header>
<h2>Most Popular Episodes</h2>
</header>
{
episodes.map((episode) => {
return (
<article>
<h2>
<a href={`/episodes/${episode.slug}`}>{episode.title}</a>
</h2>
<div set:html={episode.description} />
</article>
);
})
}
</section>
</main>
</Layout>

<style is:global>
p {
max-width: 60ch;
text-wrap: pretty;
}

header p {
margin-left: auto;
margin-right: auto;
}
</style>