chore: Run `deno fmt`

Signed-off-by: Tim Hårek Andreassen <tim@harek.no>
This commit is contained in:
Tim Hårek Andreassen 2024-03-02 18:06:38 +01:00
parent f9a97b627b
commit 047ceec147
No known key found for this signature in database
GPG Key ID: E59C7734F0E10EB5
12 changed files with 733 additions and 432 deletions

View File

@ -3,16 +3,19 @@
https://nrss.deno.dev/
## Local devlopment
install Deno
https://deno.land/manual/getting_started/installation
### Run the application
```shell
deno task start
```
Open http://localhost:8000/ to view it in the browser
## Known Problems
- Some clients may not accept a feed hosted over HTTPS only. See @steinarb's workaround [here](https://github.com/olaven/NRSS/issues/5#issuecomment-1488840679) for a possible solution.
- The feeds only provide the latest episodes for a podcast, not the entire archive. I've not yet found any acceptable method of fetching all episodes without getting rate limited by NRK or introducing a storage layer. This is tracked in https://github.com/olaven/NRSS/issues/8.

View File

@ -2,8 +2,16 @@ export default function Search(props: { defaultValue: string }) {
return (
<form className="flex flex-col w-full lg:w-2/3 mx-auto my-4">
<label className="sr-only" htmlFor="query">Program</label>
<input placeholder="NRK-podcast" className="border-2 rounded-md px-2" name="query" id="query" defaultValue={props.defaultValue} />
<button type="submit" className="my-2 bg-gray-100 rounded-md">Søk</button>
<input
placeholder="NRK-podcast"
className="border-2 rounded-md px-2"
name="query"
id="query"
defaultValue={props.defaultValue}
/>
<button type="submit" className="my-2 bg-gray-100 rounded-md">
Søk
</button>
</form>
);
}

View File

@ -1,7 +1,7 @@
import { SearchResult } from "../lib/nrk.ts"
import { SearchResult } from "../lib/nrk.ts";
import CopyButton from "../islands/CopyButton.tsx";
export function SerieCard(props: { serie: SearchResult, origin: string }) {
export function SerieCard(props: { serie: SearchResult; origin: string }) {
const feedUrl = new URL(`/api/feeds/${props.serie.seriesId}`, props.origin);
const image = props.serie.images[0];
return (

View File

@ -1,7 +1,8 @@
{
"lock": false,
"tasks": {
"start": "deno run -A --watch=static/,routes/ dev.ts"
"start": "deno run -A --watch=static/,routes/ dev.ts",
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx"
},
"imports": {
"$fresh/": "https://deno.land/x/fresh@1.6.5/",
@ -22,8 +23,8 @@
"output"
],
"useTabs": false,
"lineWidth": 80,
"indentWidth": 4,
"lineWidth": 120,
"indentWidth": 2,
"proseWrap": "preserve"
}
}

View File

@ -1,24 +1,23 @@
// DO NOT EDIT. This file is generated by fresh.
// DO NOT EDIT. This file is generated by Fresh.
// This file SHOULD be checked into source version control.
// This file is automatically updated during development when running `dev.ts`.
import config from "./deno.json" assert { type: "json" };
import * as $0 from "./routes/api/feeds/[seriesId].ts";
import * as $1 from "./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts";
import * as $2 from "./routes/index.tsx";
import * as $$0 from "./islands/CopyButton.tsx";
import * as $api_feeds_seriesId_ from "./routes/api/feeds/[seriesId].ts";
import * as $api_feeds_seriesId_episodeId_chapters from "./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts";
import * as $index from "./routes/index.tsx";
import * as $CopyButton from "./islands/CopyButton.tsx";
import { type Manifest } from "$fresh/server.ts";
const manifest = {
routes: {
"./routes/api/feeds/[seriesId].ts": $0,
"./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts": $1,
"./routes/index.tsx": $2,
"./routes/api/feeds/[seriesId].ts": $api_feeds_seriesId_,
"./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts": $api_feeds_seriesId_episodeId_chapters,
"./routes/index.tsx": $index,
},
islands: {
"./islands/CopyButton.tsx": $$0,
"./islands/CopyButton.tsx": $CopyButton,
},
baseUrl: import.meta.url,
config,
};
} satisfies Manifest;
export default manifest;

View File

@ -1,16 +1,17 @@
import Preact from "preact";
import { useState } from "preact/hooks"
import { useState } from "preact/hooks";
import { IS_BROWSER } from "$fresh/runtime.ts";
export default function CopyButton(props: { textBefore: string, textAfter: string } & Preact.PropsWithChildren) {
export default function CopyButton(
props: { textBefore: string; textAfter: string } & Preact.PropsWithChildren,
) {
const [clicked, setClicked] = useState(false);
const startReset = () => {
setTimeout(() => {
setClicked(false);
}, 2_000);
}
};
return (
<button

View File

@ -508,7 +508,9 @@ export interface components {
/** @description Aggregated info for clips */
ExtraMaterialClipsHalEmbedded: {
metadata?: {
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
displayAspectRatio: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DisplayAspectRatio"];
/**
* @description Clip length. The string is formatted as https://en.wikipedia.org/wiki/ISO_8601#Durations
* @example PT2M0S
@ -517,7 +519,9 @@ export interface components {
id: string;
/** @enum {undefined} */
playability: "nonPlayable" | "playable";
preplay: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PreplayVm"];
preplay: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PreplayVm"];
/** @description Link to full metadata */
_links: {
self: components["schemas"]["HalLink"];
@ -886,36 +890,56 @@ export interface external {
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json": {
paths: {
"/playback/debug": {
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackDebug"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackDebug"];
};
"/playback/dnslist": {
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackDnsList"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackDnsList"];
};
"/playback/manifest/{id}": {
/** This endpoint is just for clients that doesn't know its context */
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackManifestRedirect"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackManifestRedirect"];
};
"/playback/manifest/program/{programId}": {
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackProgramManifest"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackProgramManifest"];
};
"/playback/manifest/channel/{channelId}": {
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackChannelManifest"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackChannelManifest"];
};
"/playback/manifest/clip/{clipId}": {
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackClipManifest"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackClipManifest"];
};
"/playback/metadata/{id}": {
/** This endpoint is just for clients that doesn't know its context */
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackMetadataRedirect"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackMetadataRedirect"];
};
"/playback/metadata/program/{programId}": {
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackProgramMetadata"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackProgramMetadata"];
};
"/playback/metadata/channel/{channelId}": {
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackChannelMetadata"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackChannelMetadata"];
};
"/playback/metadata/clip/{clipId}": {
get: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["operations"]["getPlaybackClipMetadata"];
get: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["operations"]["getPlaybackClipMetadata"];
};
};
components: {
@ -935,8 +959,12 @@ export interface external {
href: string;
};
};
playbackData: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlaybackDataVm"];
locationData: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LocationDataVm"];
playbackData: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlaybackDataVm"];
locationData: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LocationDataVm"];
};
PlaybackDataVm: {
maxBitrate: number;
@ -951,9 +979,15 @@ export interface external {
isBeta: boolean;
originalUa: string;
wurflId: string | null;
distributionFormat: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DistributionFormat"];
deviceGroupName: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DeviceGroupName"];
playerPlatform: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayerPlatform"];
distributionFormat: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DistributionFormat"];
deviceGroupName: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DeviceGroupName"];
playerPlatform: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayerPlatform"];
customManifestParameters: (
| "AudioOnly"
| "DolbyOnly"
@ -965,7 +999,9 @@ export interface external {
| "NoSignalDiscontinuities"
| "SignalDiscontinuities"
)[];
subtitlesDistribution: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitlesDistribution"];
subtitlesDistribution: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SubtitlesDistribution"];
};
/** @enum {string} */
DistributionFormat:
@ -975,7 +1011,12 @@ export interface external {
| "ProgressiveDownload"
| "ProgressiveMp3";
/** @enum {string} */
DeviceGroupName: "Desktop" | "Mobile" | "SetTopUnit" | "Tablet" | "Tv";
DeviceGroupName:
| "Desktop"
| "Mobile"
| "SetTopUnit"
| "Tablet"
| "Tv";
/** @enum {string} */
PlayerPlatform:
| "Altibox"
@ -1021,28 +1062,51 @@ export interface external {
forwardedFor: string;
realIp: string;
};
ManifestResponse: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableManifest"]
> &
Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableManifest"]
ManifestResponse:
& Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableManifest"]
>
& Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NonPlayableManifest"]
>;
PlayableManifest: {
/** @enum {string} */
playability: "playable";
playable: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableElementVm"];
playable: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableElementVm"];
statistics: {
scores: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ScoresStatisticsVm"];
ga: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["GoogleAnalyticsVm"];
conviva: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaStatisticsVm"];
luna: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaStatisticsVm"];
scores: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ScoresStatisticsVm"];
ga: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["GoogleAnalyticsVm"];
conviva: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ConvivaStatisticsVm"];
luna: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LunaStatisticsVm"];
};
nonPlayable: unknown | null;
id: string;
streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"];
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"];
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"];
streamingMode: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableStreamingMode"];
availability: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["AvailabilityVm"];
displayAspectRatio: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DisplayAspectRatio"];
sourceMedium: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableSourceMedium"];
_links: {
self: {
href: string;
@ -1063,12 +1127,22 @@ export interface external {
ga: unknown | null;
luna: unknown | null;
};
nonPlayable: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableElementVm"];
nonPlayable: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NonPlayableElementVm"];
id: string;
streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"];
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"];
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"];
streamingMode: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableStreamingMode"];
availability: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["AvailabilityVm"];
displayAspectRatio: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DisplayAspectRatio"];
sourceMedium: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableSourceMedium"];
_links: {
self: {
href: string;
@ -1084,19 +1158,31 @@ export interface external {
endSequenceStartTime: string | null;
duration: string | null;
/** @description List of mediaAssets */
assets: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MediaUrlVm"][];
assets: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MediaUrlVm"][];
/** @description Livebuffer if element is live */
liveBuffer: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LiveBufferVm"]
> | null;
liveBuffer:
| Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LiveBufferVm"]
>
| null;
/** @description List of subtitles */
subtitles: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitlesVm"][];
subtitles: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SubtitlesVm"][];
};
MediaUrlVm: {
/** @description The media URL itself. */
url: string;
format: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableFormat"];
mimeType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableMimeType"];
format: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableFormat"];
mimeType: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableMimeType"];
/** @description True if program is marked as HDSecure, false otherwise */
encrypted: boolean;
};
@ -1120,7 +1206,9 @@ export interface external {
bufferStartTime: string | null;
/** @description Live buffer length */
bufferDuration: string;
bufferType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LiveBufferType"];
bufferType: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LiveBufferType"];
};
/**
* @description Type of liveBuffer
@ -1128,9 +1216,15 @@ export interface external {
*/
LiveBufferType: "fixed" | "growing" | "none" | "sliding";
SubtitlesVm: {
type: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleType"];
language: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleLanguage"];
label: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleLabel"];
type: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SubtitleType"];
language: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SubtitleLanguage"];
label: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SubtitleLabel"];
defaultOn: boolean;
webVtt: string;
};
@ -1139,7 +1233,10 @@ export interface external {
/** @enum {string} */
SubtitleLanguage: "en" | "nb";
/** @enum {string} */
SubtitleLabel: "English" | "Norsk tekst" | "Tekstet for hørselshemmede";
SubtitleLabel:
| "English"
| "Norsk tekst"
| "Tekstet for hørselshemmede";
ScoresStatisticsVm: {
springStreamSite: string;
springStreamStream: string;
@ -1178,14 +1275,18 @@ export interface external {
* When duration is unknown, eg channels: -1
*/
duration: number;
streamType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaStreamType"];
streamType: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ConvivaStreamType"];
/**
* @description The URL from where this video content will be downloaded. For
* manifest-based streaming protocols, it should be the URL of the top-level
* manifest.
*/
streamUrl: string;
custom: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaCustomProperties"];
custom: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ConvivaCustomProperties"];
};
/** @enum {string} */
ConvivaStreamType: "LIVE" | "VOD";
@ -1221,7 +1322,9 @@ export interface external {
* Example: underholdning
*/
category: string;
mediaType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaMediaType"];
mediaType: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ConvivaMediaType"];
/**
* @description Name of selected CDN
* Example: Telenor-Cdn
@ -1239,7 +1342,9 @@ export interface external {
* Example: tv.nrk.no, nrk.no, nrksuper.no
*/
serviceName: string;
contentType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaContentType"];
contentType: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ConvivaContentType"];
};
/**
* @description MediaType for current content
@ -1257,8 +1362,12 @@ export interface external {
| "Podcast"
| "Program";
LunaStatisticsVm: {
config: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaConfig"];
data: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaPayload"];
config: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LunaConfig"];
data: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LunaPayload"];
};
LunaConfig: {
beacon: string;
@ -1288,27 +1397,37 @@ export interface external {
* @description Only applicable for on-demand media elements. Will be null for live media
* elements.
*/
onDemand: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["OnDemandUsageRights"]
> | null;
onDemand:
| Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["OnDemandUsageRights"]
>
| null;
/**
* @description Only applicable for live media elements. Will be null for on-demand media
* elements.
*/
live:
| (Partial<{
| (
& Partial<{
/** @enum {string} */
type: "channel";
/** @enum {boolean} */
isOngoing: true;
transmissionInterval: unknown | null;
}> &
Partial<{
}>
& Partial<{
/** @enum {string} */
type: "transmission";
isOngoing: boolean;
transmissionInterval: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LiveTransmissionIntervalVm"];
}>)
transmissionInterval: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"LiveTransmissionIntervalVm"
];
}>
)
| null;
externalEmbeddingAllowed: boolean;
};
@ -1326,8 +1445,12 @@ export interface external {
/** @enum {string} */
PlayableSourceMedium: "audio" | "video";
NonPlayableElementVm: {
reason: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableReason"];
messageType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableMessageType"];
reason: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NonPlayableReason"];
messageType: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NonPlayableMessageType"];
/**
* @description Standard message for end user describing why the media element is not
* playable.
@ -1389,7 +1512,9 @@ export interface external {
/** @description Image to show before play */
PosterVm: {
/** @description List of images in different sizes */
images: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PosterImageVm"][];
images: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PosterImageVm"][];
};
/** @description Title to show before play */
TitlesVm: {
@ -1397,12 +1522,18 @@ export interface external {
subtitle: string;
};
PreplayVm: {
titles: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["TitlesVm"];
titles: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["TitlesVm"];
/** @description Description to show before play */
description: string;
poster: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PosterVm"];
poster: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PosterVm"];
/** @description List of index points */
indexPoints: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["IndexPointVm"][];
indexPoints: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["IndexPointVm"][];
};
LegalAgeRating: {
code: string;
@ -1412,7 +1543,9 @@ export interface external {
LegalAgeBodyRated: {
/** @enum {string} */
status: "rated";
rating: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeRating"];
rating: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LegalAgeRating"];
};
LegalAgeBodyExempt: {
/** @enum {string} */
@ -1420,11 +1553,16 @@ export interface external {
};
LegalAgeVm: {
legalReference: string;
body: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeBodyExempt"]
> &
Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeBodyRated"]
body:
& Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LegalAgeBodyExempt"]
>
& Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LegalAgeBodyRated"]
>;
};
PlayableMetadataVm: {
@ -1434,33 +1572,63 @@ export interface external {
PlayableMetadata: {
/** @enum {string} */
playability: "playable";
playable: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableMetadataVm"];
playable: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableMetadataVm"];
nonPlayable: unknown | null;
id: string;
streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"];
streamingMode: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableStreamingMode"];
duration: string | null;
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"];
legalAge: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"];
preplay: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PreplayVm"];
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
skipDialogInfo: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"]
> | null;
sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"];
availability: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["AvailabilityVm"];
legalAge: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LegalAgeVm"];
preplay: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PreplayVm"];
displayAspectRatio: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DisplayAspectRatio"];
skipDialogInfo:
| Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SkipDialogInfoVm"]
>
| null;
sourceMedium: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableSourceMedium"];
_links: {
self: {
href: string;
};
manifests: Partial<
manifests:
| Partial<
{
href: string;
name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"];
name: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"ManifestLinkName"
];
}[]
> | null;
next: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NextLink"]
> | null;
series?: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SeriesLink"];
>
| null;
next:
| Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NextLink"]
>
| null;
series?: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SeriesLink"];
progress?: {
href: string;
/** @enum {boolean} */
@ -1468,50 +1636,90 @@ export interface external {
};
};
_embedded: {
manifests: Partial<
manifests:
| Partial<
{
id: string;
availabilityLabel: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityLabel"];
availabilityLabel: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"AvailabilityLabel"
];
_links: {
self: {
href: string;
name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"];
name: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"ManifestLinkName"
];
};
};
}[]
> | null;
>
| null;
};
};
NonPlayableMetadata: {
/** @enum {string} */
playability: "nonPlayable";
playable: unknown | null;
nonPlayable: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableElementVm"];
nonPlayable: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NonPlayableElementVm"];
id: string;
streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"];
streamingMode: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableStreamingMode"];
duration: string | null;
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"];
legalAge: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"];
preplay: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PreplayVm"];
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
skipDialogInfo: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"]
> | null;
sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"];
availability: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["AvailabilityVm"];
legalAge: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["LegalAgeVm"];
preplay: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PreplayVm"];
displayAspectRatio: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DisplayAspectRatio"];
skipDialogInfo:
| Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SkipDialogInfoVm"]
>
| null;
sourceMedium: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableSourceMedium"];
_links: {
self: {
href: string;
};
manifests: Partial<
manifests:
| Partial<
{
href: string;
name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"];
name: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"ManifestLinkName"
];
}[]
> | null;
next: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NextLink"]
> | null;
series?: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SeriesLink"];
>
| null;
next:
| Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NextLink"]
>
| null;
series?: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SeriesLink"];
progress?: {
href: string;
/** @enum {boolean} */
@ -1519,25 +1727,40 @@ export interface external {
};
};
_embedded: {
manifests: Partial<
manifests:
| Partial<
{
id: string;
availabilityLabel: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityLabel"];
availabilityLabel: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"AvailabilityLabel"
];
_links: {
self: {
href: string;
name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"];
name: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"ManifestLinkName"
];
};
};
}[]
> | null;
>
| null;
};
};
MetadataResponse: Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableMetadata"]
> &
Partial<
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableMetadata"]
MetadataResponse:
& Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableMetadata"]
>
& Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NonPlayableMetadata"]
>;
};
};
@ -1547,7 +1770,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DebugData"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DebugData"];
};
};
};
@ -1557,7 +1782,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DnsList"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DnsList"];
};
};
};
@ -1593,7 +1820,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestResponse"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ManifestResponse"];
};
};
};
@ -1623,7 +1852,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestResponse"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ManifestResponse"];
};
};
};
@ -1651,7 +1882,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestResponse"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ManifestResponse"];
};
};
};
@ -1679,7 +1912,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestResponse"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ManifestResponse"];
};
};
};
@ -1709,7 +1944,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MetadataResponse"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MetadataResponse"];
};
};
};
@ -1729,7 +1966,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MetadataResponse"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MetadataResponse"];
};
};
};
@ -1749,7 +1988,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MetadataResponse"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MetadataResponse"];
};
};
};
@ -1771,7 +2012,9 @@ export interface external {
/** OK */
200: {
content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MetadataResponse"];
"application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MetadataResponse"];
};
};
};

View File

@ -1,20 +1,21 @@
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 catalogComponents } from './nrk-catalog.ts';
import { components as searchComponents } from "./nrk-search.ts";
import { components as catalogComponents } from "./nrk-catalog.ts";
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never
export type Serie = catalogComponents["schemas"]["SeriesViewModel"]
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
export type Serie = catalogComponents["schemas"]["SeriesViewModel"];
type OriginalEpisode = catalogComponents["schemas"]["EpisodeHalResource"];
export type OriginalEpisode = OriginalEpisode & { url: string; }
export type OriginalEpisode = OriginalEpisode & { url: string };
export type SearchResultList = searchComponents["schemas"]["seriesResult"]["results"];
export type SearchResult = ArrayElement<SearchResultList> & { description?: string }
export type SearchResult = ArrayElement<SearchResultList> & {
description?: string;
};
/* Incomplete manifest types */
interface Manifest {
id: string;
playable: {
endSequenceStartTime: null,
endSequenceStartTime: null;
duration: string;
assets: {
url: string;
@ -25,58 +26,70 @@ interface Manifest {
};
}
async function withDownloadLink(episode: OriginalEpisode): Promise<OriginalEpisode> {
async function withDownloadLink(
episode: OriginalEpisode,
): Promise<OriginalEpisode> {
// getting stream link
const [playbackStatus, playbackResponse] = await get<Manifest>(`https://psapi.nrk.no/playback/manifest/podcast/${episode.episodeId}`)
const [playbackStatus, playbackResponse] = await get<Manifest>(
`https://psapi.nrk.no/playback/manifest/podcast/${episode.episodeId}`,
);
if (playbackStatus === OK && playbackResponse) {
return { ...episode, url: playbackResponse.playable.assets[0].url };
} else {
throw `Error getting downloadLink for ${episode.episodeId}, serie: ${episode.originalTitle}. Status: ${playbackStatus}`
throw `Error getting downloadLink for ${episode.episodeId}, serie: ${episode.originalTitle}. Status: ${playbackStatus}`;
}
}
export const nrkRadio = {
search: async (query: string): Promise<SearchResultList> => {
const [status, response] = await get<searchComponents["schemas"]["searchresult"]>(`https://psapi.nrk.no/radio/search/search?q=${query}`);
const [status, response] = await get<
searchComponents["schemas"]["searchresult"]
>(`https://psapi.nrk.no/radio/search/search?q=${query}`);
if (status === OK && response) {
return response.results.series?.results;
} else {
throw `Something went wrong with ${query} - got status ${status}`
throw `Something went wrong with ${query} - got status ${status}`;
}
},
getSerieData: async (seriesId: string) => {
const [
[episodeStatus, episodeResponse],
[seriesStatus, serieResponse],
] = await Promise.all([
get<catalogComponents["schemas"]["EpisodesHalResource"]>(`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes`),
get<catalogComponents["schemas"]["SeriesHalResource"]>(`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}`)
get<catalogComponents["schemas"]["EpisodesHalResource"]>(
`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes`,
),
get<catalogComponents["schemas"]["SeriesHalResource"]>(
`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}`,
),
]);
if (episodeStatus === OK && seriesStatus === OK && serieResponse?.series && episodeResponse?._embedded.episodes?.length) {
const episodes = await Promise.all(episodeResponse._embedded.episodes.map(episode => withDownloadLink(episode)));
if (
episodeStatus === OK && seriesStatus === OK &&
serieResponse?.series && episodeResponse?._embedded.episodes?.length
) {
const episodes = await Promise.all(
episodeResponse._embedded.episodes.map((episode) => withDownloadLink(episode)),
);
return {
...serieResponse.series,
episodes,
}
};
} else {
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<OriginalEpisode> => {
const url = `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`
getEpisode: async (
seriesId: string,
episodeId: string,
): Promise<OriginalEpisode> => {
const url = `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`;
const [status, episode] = await get<OriginalEpisode>(url);
if (status !== OK) {
throw `Error getting episode ${episodeId}. Status: ${status}. Series: ${seriesId}`;
}
return episode;
}
}
},
};

View File

@ -1,11 +1,10 @@
import { nrkRadio, Episode } from "../../../lib/nrk.ts"
import { Episode, nrkRadio } from "../../../lib/nrk.ts";
import { HandlerContext } from "$fresh/server.ts";
import { serialize, tag, declaration } from "https://raw.githubusercontent.com/olaven/serialize-xml/v0.4.0/mod.ts";
import { declaration, serialize, tag } from "https://raw.githubusercontent.com/olaven/serialize-xml/v0.4.0/mod.ts";
import { getHostName } from "../../../utils.ts";
function toItemTag(seriesId: string, episode: Episode) {
const description = episode.titles.subtitle || "";
return tag("item", [
tag("title", episode.titles.title),
@ -16,7 +15,10 @@ function toItemTag(seriesId: string, episode: Episode) {
tag("pubDate", new Date(episode.date).toUTCString()),
tag("itunes:duration", episode.durationInSeconds.toString()),
tag("podcast:chapters", "", [
["url", `${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`],
[
"url",
`${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`,
],
["type", "application/json+chapters"],
]),
tag("enclosure", "", [
@ -24,11 +26,11 @@ function toItemTag(seriesId: string, episode: Episode) {
["length", episode.durationInSeconds.toString()],
["type", "audio/mpeg3"],
]),
])
]);
}
async function buildFeed(seriesId: string) {
const serie = await nrkRadio.getSerieData(seriesId)
const serie = await nrkRadio.getSerieData(seriesId);
const imageUrl = serie.squareImage.at(-1)?.url;
const linkValue = `https://radio.nrk.no/podkast/${serie.id}`;
@ -51,18 +53,17 @@ async function buildFeed(seriesId: string) {
podcast feed validation.
*/
tag("itunes:category", "", [["text", "Government"]]),
tag("itunes:owner",
[
tag("itunes:owner", [
tag("itunes:name", "NRK"),
tag("itunes:email", "nrkpodcast@nrk.no")
]
),
tag("itunes:email", "nrkpodcast@nrk.no"),
]),
tag(
"description",
serie.titles.subtitle || ""
serie.titles.subtitle || "",
),
tag("ttl", "60"), //60 minutes
...(imageUrl ? [
...(imageUrl
? [
tag("itunes:image", "", [
["href", imageUrl],
]),
@ -70,9 +71,10 @@ async function buildFeed(seriesId: string) {
tag("url", imageUrl),
tag("title", serie.titles.title),
tag("link", linkValue),
])
] : []),
...serie.episodes.map(episode => toItemTag(seriesId, episode)),
]),
]
: []),
...serie.episodes.map((episode) => toItemTag(seriesId, episode)),
]),
],
[
@ -80,18 +82,21 @@ async function buildFeed(seriesId: string) {
["xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd"],
["xmlns:content", "http://purl.org/rss/1.0/modules/content/"],
["xmlns:podcast", "https://podcastindex.org/namespace/1.0"],
]
)
],
),
);
}
export const handler = async (req: Request, _ctx: HandlerContext): Promise<Response> => {
export const handler = async (
req: Request,
_ctx: HandlerContext,
): Promise<Response> => {
const seriesId = _ctx.params.seriesId;
const feedContent = await buildFeed(seriesId);
return new Response(feedContent, {
headers: {
"Content-Type": "application/xml"
}
"Content-Type": "application/xml",
},
});
};

View File

@ -2,12 +2,15 @@ import { parse, toSeconds } from "https://esm.sh/iso8601-duration@2.1.1";
import { nrkRadio, OriginalEpisode } from "../../../../../lib/nrk.ts";
function toChapters(episode: OriginalEpisode) {
return episode.indexPoints.map(indexPoint => ({
return episode.indexPoints.map((indexPoint) => ({
title: indexPoint.title,
startTime: toSeconds(parse(indexPoint.startPoint))
}))
startTime: toSeconds(parse(indexPoint.startPoint)),
}));
}
export const handler = async (req: Request, _ctx: HandlerContext): Promise<Response> => {
export const handler = async (
req: Request,
_ctx: HandlerContext,
): Promise<Response> => {
const seriesId = _ctx.params.seriesId;
const episodeId = _ctx.params.episodeId;
@ -17,11 +20,11 @@ export const handler = async (req: Request, _ctx: HandlerContext): Promise<Respo
const body = {
version: "1.2.0",
chapters,
}
};
return new Response(JSON.stringify(body), {
headers: {
"Content-Type": "application/json"
}
"Content-Type": "application/json",
},
});
};

View File

@ -1,6 +1,6 @@
import { Head } from "$fresh/runtime.ts";
import { Handlers, PageProps } from "$fresh/server.ts";
import Search from "../components/Search.tsx"
import Search from "../components/Search.tsx";
import { SerieCard } from "../components/SeriesCard.tsx";
import { nrkRadio, SearchResult, SearchResultList, Serie } from "../lib/nrk.ts";
@ -34,15 +34,42 @@ export default function Home({ data }: PageProps<HandlerData>) {
<h2 className="lg:text-xl">NRK-podcast som RSS</h2>
</div>
<Search defaultValue={data.query} />
{data.result ? (
{data.result
? (
<div className="w-full mx-auto my-4">
{data.result.map(result => <SerieCard serie={result} origin={data.origin} />)}
{data.result.map((result) => (
<SerieCard
serie={result}
origin={data.origin}
/>
))}
</div>
) : null}
)
: null}
<h2 className="text-3xl">Hva er dette?</h2>
<p>Hei! Denne løsningen er laget som en reaksjon at statsfinansierte NRK lukker ned innholdet sitt til sin egen app fremfor å bygge oppunder åpne standarder som RSS. Denne siden er laget fort og gæli - den er ustabil og kommer til å krasje :) Sjekk gjerne ut <a href="https://github.com/olaven/NRSS/" className="underline">kildekoden</a>. </p>
<p>
Hei! Denne løsningen er laget som en reaksjon at statsfinansierte NRK lukker ned innholdet sitt til sin
egen app fremfor å bygge oppunder åpne standarder som RSS. Denne siden er laget fort og gæli - den er ustabil
og kommer til å krasje :) Sjekk gjerne ut{" "}
<a
href="https://github.com/olaven/NRSS/"
className="underline"
>
kildekoden
</a>.
</p>
<h2 className="text-3xl">Hvordan bruker jeg dette?</h2>
<p>Søk NRK-podcasten du vil høre . Kopier deretter URL-en under bildet. Lim denne inn i akkurat den podcastspilleren du måtte foretrekke! Se <a className="underline" href="https://help.omnystudio.com/en/articles/5222518-podcast-apps-that-support-add-rss-feed">her</a> for en oversikt over hvordan det gjøres i populære podcastspillere.</p>
<p>
Søk NRK-podcasten du vil høre . Kopier deretter URL-en under bildet. Lim denne inn i akkurat den
podcastspilleren du måtte foretrekke! Se{" "}
<a
className="underline"
href="https://help.omnystudio.com/en/articles/5222518-podcast-apps-that-support-add-rss-feed"
>
her
</a>{" "}
for en oversikt over hvordan det gjøres i populære podcastspillere.
</p>
</div>
</>
);

View File

@ -4,8 +4,6 @@ export function getHostName() {
return `https://nrss-${deploymentId}.deno.dev`;
} else {
// assume env
return 'http://localhost:8000';
return "http://localhost:8000";
}
}