moved parse function to nrk.ts
This commit is contained in:
parent
ae50117ce1
commit
f7813da81f
|
|
@ -1,27 +1,22 @@
|
||||||
import { nrkRadio } from "./nrk/nrk.ts";
|
import { nrkRadio } from "./nrk/nrk.ts";
|
||||||
import { parse } from "./parse.ts";
|
|
||||||
import { Series, storage } from "./storage.ts";
|
import { Series, storage } from "./storage.ts";
|
||||||
import * as datetime from "datetime";
|
import * as datetime from "datetime";
|
||||||
|
|
||||||
const SYNC_INTERVAL_HOURS = 1;
|
const SYNC_INTERVAL_HOURS = 1;
|
||||||
|
|
||||||
async function initialFetch(options: { id: string }) {
|
async function initialFetch(options: { id: string }) {
|
||||||
const fromNrk = await nrkRadio.getSerieData(options.id);
|
const series = await nrkRadio.getSeries(options.id);
|
||||||
const parsed = parse.series(fromNrk);
|
const stored = storage.write(series);
|
||||||
|
|
||||||
const stored = storage.write(parsed);
|
|
||||||
if (!stored) {
|
if (!stored) {
|
||||||
throw new Error(`Failed to store series ${options.id}`);
|
throw new Error(`Failed to store series ${options.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed;
|
return series;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateFetch(existingSeries: Series) {
|
async function updateFetch(existingSeries: Series) {
|
||||||
const fromNrk = await nrkRadio.getSerieData(existingSeries.id);
|
const series = await nrkRadio.getSeries(existingSeries.id);
|
||||||
const parsed = parse.series(fromNrk);
|
const newEpisodes = series.episodes.filter((episode) => {
|
||||||
|
|
||||||
const newEpisodes = parsed.episodes.filter((episode) => {
|
|
||||||
return !existingSeries.episodes.find((serieEpisode) => serieEpisode.id === episode.id);
|
return !existingSeries.episodes.find((serieEpisode) => serieEpisode.id === episode.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,28 +2,54 @@ import { get, OK } from "https://deno.land/x/kall@v0.1.0/mod.ts";
|
||||||
import { components as searchComponents } from "./nrk-search.ts";
|
import { components as searchComponents } from "./nrk-search.ts";
|
||||||
import { components as catalogComponents } from "./nrk-catalog.ts";
|
import { components as catalogComponents } from "./nrk-catalog.ts";
|
||||||
import { external as playbackComponents } from "./nrk-playback.ts";
|
import { external as playbackComponents } from "./nrk-playback.ts";
|
||||||
|
import { Series } from "../storage.ts";
|
||||||
|
|
||||||
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
|
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
|
||||||
type PodcastEpisodes = catalogComponents["schemas"]["EpisodesHalResource"];
|
type PodcastEpisodes = catalogComponents["schemas"]["EpisodesHalResource"];
|
||||||
type Podcast = catalogComponents["schemas"]["SeriesHalResource"];
|
type Podcast = catalogComponents["schemas"]["SeriesHalResource"];
|
||||||
type PodcastEpisodesSingle = catalogComponents["schemas"]["EpisodeHalResource"];
|
type PodcastEpisodesSingle = catalogComponents["schemas"]["EpisodeHalResource"];
|
||||||
|
|
||||||
export type Serie = catalogComponents["schemas"]["SeriesViewModel"];
|
export type NrkSerie = catalogComponents["schemas"]["SeriesViewModel"];
|
||||||
export type OriginalEpisode = PodcastEpisodesSingle & { url: string };
|
export type NrkOriginalEpisode = PodcastEpisodesSingle & { url: string };
|
||||||
export type PodcastEpisode = catalogComponents["schemas"]["PodcastEpisodeHalResource"];
|
export type NrkPodcastEpisode = catalogComponents["schemas"]["PodcastEpisodeHalResource"];
|
||||||
export type SearchResultList = searchComponents["schemas"]["seriesResult"]["results"];
|
export type NrkSearchResultList = searchComponents["schemas"]["seriesResult"]["results"];
|
||||||
export type SearchResult = ArrayElement<SearchResultList> & {
|
export type SearchResult = ArrayElement<NrkSearchResultList> & {
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Manifest = playbackComponents["schemas/playback-channel.json"]["components"]["schemas"]["PlayableManifest"];
|
type Manifest = playbackComponents["schemas/playback-channel.json"]["components"]["schemas"]["PlayableManifest"];
|
||||||
|
type NrkSerieData = Awaited<ReturnType<typeof nrkRadio.getSeries>>;
|
||||||
|
|
||||||
|
function parseSeries(nrkSeries: NrkSerieData): Series {
|
||||||
|
const imageUrl = nrkSeries.squareImage?.at(-1)?.url ?? "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: nrkSeries.id,
|
||||||
|
title: nrkSeries.titles.title,
|
||||||
|
subtitle: nrkSeries.titles.subtitle ?? null,
|
||||||
|
link: `https://radio.nrk.no/podkast/${nrkSeries.id}`,
|
||||||
|
imageUrl: imageUrl,
|
||||||
|
lastFetchedAt: new Date(),
|
||||||
|
episodes: nrkSeries.episodes.map((episode) => {
|
||||||
|
return {
|
||||||
|
id: episode.id,
|
||||||
|
title: episode.titles.title,
|
||||||
|
subtitle: episode.titles.subtitle ?? null,
|
||||||
|
url: episode.url,
|
||||||
|
shareLink: episode._links.share?.href ?? "",
|
||||||
|
date: new Date(episode.date),
|
||||||
|
durationInSeconds: episode.durationInSeconds,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const nrkAPI = `https://psapi.nrk.no`;
|
const nrkAPI = `https://psapi.nrk.no`;
|
||||||
|
|
||||||
async function withDownloadLink(
|
async function withDownloadLink(
|
||||||
episode: PodcastEpisodesSingle,
|
episode: PodcastEpisodesSingle,
|
||||||
type: catalogComponents["schemas"]["Type"],
|
type: catalogComponents["schemas"]["Type"],
|
||||||
): Promise<OriginalEpisode> {
|
): Promise<NrkOriginalEpisode> {
|
||||||
// getting stream link
|
// getting stream link
|
||||||
let [playbackStatus, playbackResponse] = await get<Manifest>(
|
let [playbackStatus, playbackResponse] = await get<Manifest>(
|
||||||
`${nrkAPI}/playback/manifest/podcast/${episode.episodeId}`,
|
`${nrkAPI}/playback/manifest/podcast/${episode.episodeId}`,
|
||||||
|
|
@ -43,7 +69,7 @@ async function withDownloadLink(
|
||||||
}
|
}
|
||||||
|
|
||||||
export const nrkRadio = {
|
export const nrkRadio = {
|
||||||
search: async (query: string): Promise<SearchResultList> => {
|
search: async (query: string): Promise<NrkSearchResultList> => {
|
||||||
const [status, response] = await get<
|
const [status, response] = await get<
|
||||||
searchComponents["schemas"]["searchresult"]
|
searchComponents["schemas"]["searchresult"]
|
||||||
>(`${nrkAPI}/radio/search/search?q=${query}`);
|
>(`${nrkAPI}/radio/search/search?q=${query}`);
|
||||||
|
|
@ -52,7 +78,7 @@ export const nrkRadio = {
|
||||||
}
|
}
|
||||||
throw `Something went wrong with ${query} - got status ${status}`;
|
throw `Something went wrong with ${query} - got status ${status}`;
|
||||||
},
|
},
|
||||||
getSerieData: async (seriesId: string) => {
|
getSeries: async (seriesId: string) => {
|
||||||
let [
|
let [
|
||||||
[episodeStatus, episodeResponse],
|
[episodeStatus, episodeResponse],
|
||||||
[seriesStatus, serieResponse],
|
[seriesStatus, serieResponse],
|
||||||
|
|
@ -86,16 +112,20 @@ export const nrkRadio = {
|
||||||
const episodes = await Promise.all(
|
const episodes = await Promise.all(
|
||||||
episodeResponse._embedded.episodes.map((episode) => withDownloadLink(episode, serieResponse.type)),
|
episodeResponse._embedded.episodes.map((episode) => withDownloadLink(episode, serieResponse.type)),
|
||||||
);
|
);
|
||||||
return {
|
|
||||||
|
const serieData = {
|
||||||
...serieResponse.series,
|
...serieResponse.series,
|
||||||
episodes,
|
episodes,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parsedSeries = parseSeries(serieData);
|
||||||
|
return parsedSeries;
|
||||||
}
|
}
|
||||||
throw `Error getting episodes for ${seriesId}: EpisodeStatus: ${episodeStatus}. SerieStatus: ${seriesStatus}`;
|
throw `Error getting episodes for ${seriesId}: EpisodeStatus: ${episodeStatus}. SerieStatus: ${seriesStatus}`;
|
||||||
},
|
},
|
||||||
getEpisode: async (seriesId: string, episodeId: string): Promise<PodcastEpisode | null> => {
|
getEpisode: async (seriesId: string, episodeId: string): Promise<NrkPodcastEpisode | null> => {
|
||||||
const url = `${nrkAPI}/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`;
|
const url = `${nrkAPI}/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`;
|
||||||
const [status, episode] = await get<PodcastEpisode>(url);
|
const [status, episode] = await get<NrkPodcastEpisode>(url);
|
||||||
if (status === OK && episode) {
|
if (status === OK && episode) {
|
||||||
return episode;
|
return episode;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
lib/parse.ts
31
lib/parse.ts
|
|
@ -1,31 +0,0 @@
|
||||||
import { nrkRadio } from "./nrk/nrk.ts";
|
|
||||||
import { Series } from "./storage.ts";
|
|
||||||
|
|
||||||
type NrkSerieData = Awaited<ReturnType<typeof nrkRadio.getSerieData>>;
|
|
||||||
function series(nrkSeries: NrkSerieData): Series {
|
|
||||||
const imageUrl = nrkSeries.squareImage?.at(-1)?.url ?? "";
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: nrkSeries.id,
|
|
||||||
title: nrkSeries.titles.title,
|
|
||||||
subtitle: nrkSeries.titles.subtitle ?? null,
|
|
||||||
link: `https://radio.nrk.no/podkast/${nrkSeries.id}`,
|
|
||||||
imageUrl: imageUrl,
|
|
||||||
lastFetchedAt: new Date(),
|
|
||||||
episodes: nrkSeries.episodes.map((episode) => {
|
|
||||||
return {
|
|
||||||
id: episode.id,
|
|
||||||
title: episode.titles.title,
|
|
||||||
subtitle: episode.titles.subtitle ?? null,
|
|
||||||
url: episode.url,
|
|
||||||
shareLink: episode._links.share?.href ?? "",
|
|
||||||
date: new Date(episode.date),
|
|
||||||
durationInSeconds: episode.durationInSeconds,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const parse = {
|
|
||||||
series,
|
|
||||||
};
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { FreshContext } from "$fresh/server.ts";
|
import { FreshContext } from "$fresh/server.ts";
|
||||||
import { parse, toSeconds } from "https://esm.sh/iso8601-duration@2.1.1";
|
import { parse, toSeconds } from "https://esm.sh/iso8601-duration@2.1.1";
|
||||||
import { nrkRadio, PodcastEpisode } from "../../../../../lib/nrk/nrk.ts";
|
import { NrkPodcastEpisode, nrkRadio } from "../../../../../lib/nrk/nrk.ts";
|
||||||
|
|
||||||
type Chapter = {
|
type Chapter = {
|
||||||
title: string | undefined;
|
title: string | undefined;
|
||||||
startTime: number | undefined;
|
startTime: number | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
function toChapters(episode: PodcastEpisode): Chapter[] | null {
|
function toChapters(episode: NrkPodcastEpisode): Chapter[] | null {
|
||||||
if (!episode.indexPoints) {
|
if (!episode.indexPoints) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,20 @@ import Header from "../components/Header.tsx";
|
||||||
import Search from "../components/Search.tsx";
|
import Search from "../components/Search.tsx";
|
||||||
import SeriesCard from "../components/SeriesCard.tsx";
|
import SeriesCard from "../components/SeriesCard.tsx";
|
||||||
import { CSS, render } from "$gfm";
|
import { CSS, render } from "$gfm";
|
||||||
import { nrkRadio, SearchResultList } from "../lib/nrk/nrk.ts";
|
import { nrkRadio, NrkSearchResultList } from "../lib/nrk/nrk.ts";
|
||||||
|
|
||||||
interface HandlerData {
|
interface HandlerData {
|
||||||
query: string;
|
query: string;
|
||||||
origin: string;
|
origin: string;
|
||||||
rawMarkdown: string;
|
rawMarkdown: string;
|
||||||
result?: SearchResultList;
|
result?: NrkSearchResultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handler: Handlers<HandlerData> = {
|
export const handler: Handlers<HandlerData> = {
|
||||||
async GET(request, ctx) {
|
async GET(request, ctx) {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const query = url.searchParams.get("query");
|
const query = url.searchParams.get("query");
|
||||||
let result: SearchResultList | undefined;
|
let result: NrkSearchResultList | undefined;
|
||||||
if (query) {
|
if (query) {
|
||||||
result = await nrkRadio.search(query);
|
result = await nrkRadio.search(query);
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ export default function Home({ data }: PageProps<HandlerData>) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SearchResult({ result, origin }: { result: SearchResultList; origin: string }) {
|
function SearchResult({ result, origin }: { result: NrkSearchResultList; origin: string }) {
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue