Upgrade Fresh and add a bunch of small QOL improvements #16
|
|
@ -3,16 +3,19 @@
|
||||||
https://nrss.deno.dev/
|
https://nrss.deno.dev/
|
||||||
|
|
||||||
## Local devlopment
|
## Local devlopment
|
||||||
|
|
||||||
install Deno
|
install Deno
|
||||||
https://deno.land/manual/getting_started/installation
|
https://deno.land/manual/getting_started/installation
|
||||||
|
|
||||||
### Run the application
|
### Run the application
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
deno task start
|
deno task start
|
||||||
```
|
```
|
||||||
|
|
||||||
Open http://localhost:8000/ to view it in the browser
|
Open http://localhost:8000/ to view it in the browser
|
||||||
|
|
||||||
|
|
||||||
## Known Problems
|
## 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.
|
- 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.
|
- 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.
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,16 @@ export default function Search(props: { defaultValue: string }) {
|
||||||
return (
|
return (
|
||||||
<form className="flex flex-col w-full lg:w-2/3 mx-auto my-4">
|
<form className="flex flex-col w-full lg:w-2/3 mx-auto my-4">
|
||||||
<label className="sr-only" htmlFor="query">Program</label>
|
<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} />
|
<input
|
||||||
<button type="submit" className="my-2 bg-gray-100 rounded-md">Søk</button>
|
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>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { SearchResult } from "../lib/nrk.ts"
|
import { SearchResult } from "../lib/nrk.ts";
|
||||||
import CopyButton from "../islands/CopyButton.tsx";
|
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 feedUrl = new URL(`/api/feeds/${props.serie.seriesId}`, props.origin);
|
||||||
const image = props.serie.images[0];
|
const image = props.serie.images[0];
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"lock": false,
|
"lock": false,
|
||||||
"tasks": {
|
"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": {
|
"imports": {
|
||||||
"$fresh/": "https://deno.land/x/fresh@1.6.5/",
|
"$fresh/": "https://deno.land/x/fresh@1.6.5/",
|
||||||
|
|
@ -22,8 +23,8 @@
|
||||||
"output"
|
"output"
|
||||||
],
|
],
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"lineWidth": 80,
|
"lineWidth": 120,
|
||||||
"indentWidth": 4,
|
"indentWidth": 2,
|
||||||
"proseWrap": "preserve"
|
"proseWrap": "preserve"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
23
fresh.gen.ts
23
fresh.gen.ts
|
|
@ -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 SHOULD be checked into source version control.
|
||||||
// This file is automatically updated during development when running `dev.ts`.
|
// This file is automatically updated during development when running `dev.ts`.
|
||||||
|
|
||||||
import config from "./deno.json" assert { type: "json" };
|
import * as $api_feeds_seriesId_ from "./routes/api/feeds/[seriesId].ts";
|
||||||
import * as $0 from "./routes/api/feeds/[seriesId].ts";
|
import * as $api_feeds_seriesId_episodeId_chapters from "./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts";
|
||||||
import * as $1 from "./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts";
|
import * as $index from "./routes/index.tsx";
|
||||||
import * as $2 from "./routes/index.tsx";
|
import * as $CopyButton from "./islands/CopyButton.tsx";
|
||||||
import * as $$0 from "./islands/CopyButton.tsx";
|
import { type Manifest } from "$fresh/server.ts";
|
||||||
|
|
||||||
const manifest = {
|
const manifest = {
|
||||||
routes: {
|
routes: {
|
||||||
"./routes/api/feeds/[seriesId].ts": $0,
|
"./routes/api/feeds/[seriesId].ts": $api_feeds_seriesId_,
|
||||||
"./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts": $1,
|
"./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts": $api_feeds_seriesId_episodeId_chapters,
|
||||||
"./routes/index.tsx": $2,
|
"./routes/index.tsx": $index,
|
||||||
},
|
},
|
||||||
islands: {
|
islands: {
|
||||||
"./islands/CopyButton.tsx": $$0,
|
"./islands/CopyButton.tsx": $CopyButton,
|
||||||
},
|
},
|
||||||
baseUrl: import.meta.url,
|
baseUrl: import.meta.url,
|
||||||
config,
|
} satisfies Manifest;
|
||||||
};
|
|
||||||
|
|
||||||
export default manifest;
|
export default manifest;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
import Preact from "preact";
|
import Preact from "preact";
|
||||||
import { useState } from "preact/hooks"
|
import { useState } from "preact/hooks";
|
||||||
import { IS_BROWSER } from "$fresh/runtime.ts";
|
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 [clicked, setClicked] = useState(false);
|
||||||
|
|
||||||
const startReset = () => {
|
const startReset = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setClicked(false);
|
setClicked(false);
|
||||||
}, 2_000);
|
}, 2_000);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -508,7 +508,9 @@ export interface components {
|
||||||
/** @description Aggregated info for clips */
|
/** @description Aggregated info for clips */
|
||||||
ExtraMaterialClipsHalEmbedded: {
|
ExtraMaterialClipsHalEmbedded: {
|
||||||
metadata?: {
|
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
|
* @description Clip length. The string is formatted as https://en.wikipedia.org/wiki/ISO_8601#Durations
|
||||||
* @example PT2M0S
|
* @example PT2M0S
|
||||||
|
|
@ -517,7 +519,9 @@ export interface components {
|
||||||
id: string;
|
id: string;
|
||||||
/** @enum {undefined} */
|
/** @enum {undefined} */
|
||||||
playability: "nonPlayable" | "playable";
|
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 */
|
/** @description Link to full metadata */
|
||||||
_links: {
|
_links: {
|
||||||
self: components["schemas"]["HalLink"];
|
self: components["schemas"]["HalLink"];
|
||||||
|
|
@ -886,36 +890,56 @@ export interface external {
|
||||||
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json": {
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json": {
|
||||||
paths: {
|
paths: {
|
||||||
"/playback/debug": {
|
"/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": {
|
"/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}": {
|
"/playback/manifest/{id}": {
|
||||||
/** This endpoint is just for clients that doesn't know its context */
|
/** 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}": {
|
"/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}": {
|
"/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}": {
|
"/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}": {
|
"/playback/metadata/{id}": {
|
||||||
/** This endpoint is just for clients that doesn't know its context */
|
/** 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}": {
|
"/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}": {
|
"/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}": {
|
"/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: {
|
components: {
|
||||||
|
|
@ -935,8 +959,12 @@ export interface external {
|
||||||
href: string;
|
href: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
playbackData: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlaybackDataVm"];
|
playbackData: external[
|
||||||
locationData: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LocationDataVm"];
|
"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: {
|
PlaybackDataVm: {
|
||||||
maxBitrate: number;
|
maxBitrate: number;
|
||||||
|
|
@ -951,9 +979,15 @@ export interface external {
|
||||||
isBeta: boolean;
|
isBeta: boolean;
|
||||||
originalUa: string;
|
originalUa: string;
|
||||||
wurflId: string | null;
|
wurflId: string | null;
|
||||||
distributionFormat: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DistributionFormat"];
|
distributionFormat: external[
|
||||||
deviceGroupName: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DeviceGroupName"];
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
playerPlatform: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayerPlatform"];
|
]["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: (
|
customManifestParameters: (
|
||||||
| "AudioOnly"
|
| "AudioOnly"
|
||||||
| "DolbyOnly"
|
| "DolbyOnly"
|
||||||
|
|
@ -965,7 +999,9 @@ export interface external {
|
||||||
| "NoSignalDiscontinuities"
|
| "NoSignalDiscontinuities"
|
||||||
| "SignalDiscontinuities"
|
| "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} */
|
/** @enum {string} */
|
||||||
DistributionFormat:
|
DistributionFormat:
|
||||||
|
|
@ -975,7 +1011,12 @@ export interface external {
|
||||||
| "ProgressiveDownload"
|
| "ProgressiveDownload"
|
||||||
| "ProgressiveMp3";
|
| "ProgressiveMp3";
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
DeviceGroupName: "Desktop" | "Mobile" | "SetTopUnit" | "Tablet" | "Tv";
|
DeviceGroupName:
|
||||||
|
| "Desktop"
|
||||||
|
| "Mobile"
|
||||||
|
| "SetTopUnit"
|
||||||
|
| "Tablet"
|
||||||
|
| "Tv";
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
PlayerPlatform:
|
PlayerPlatform:
|
||||||
| "Altibox"
|
| "Altibox"
|
||||||
|
|
@ -1021,28 +1062,51 @@ export interface external {
|
||||||
forwardedFor: string;
|
forwardedFor: string;
|
||||||
realIp: string;
|
realIp: string;
|
||||||
};
|
};
|
||||||
ManifestResponse: Partial<
|
ManifestResponse:
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableManifest"]
|
& Partial<
|
||||||
> &
|
external[
|
||||||
Partial<
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableManifest"]
|
]["components"]["schemas"]["PlayableManifest"]
|
||||||
|
>
|
||||||
|
& Partial<
|
||||||
|
external[
|
||||||
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
|
]["components"]["schemas"]["NonPlayableManifest"]
|
||||||
>;
|
>;
|
||||||
PlayableManifest: {
|
PlayableManifest: {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
playability: "playable";
|
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: {
|
statistics: {
|
||||||
scores: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ScoresStatisticsVm"];
|
scores: external[
|
||||||
ga: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["GoogleAnalyticsVm"];
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
conviva: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaStatisticsVm"];
|
]["components"]["schemas"]["ScoresStatisticsVm"];
|
||||||
luna: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaStatisticsVm"];
|
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;
|
nonPlayable: unknown | null;
|
||||||
id: string;
|
id: string;
|
||||||
streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"];
|
streamingMode: external[
|
||||||
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"];
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
|
]["components"]["schemas"]["PlayableStreamingMode"];
|
||||||
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"];
|
||||||
|
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: {
|
_links: {
|
||||||
self: {
|
self: {
|
||||||
href: string;
|
href: string;
|
||||||
|
|
@ -1063,12 +1127,22 @@ export interface external {
|
||||||
ga: unknown | null;
|
ga: unknown | null;
|
||||||
luna: 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;
|
id: string;
|
||||||
streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"];
|
streamingMode: external[
|
||||||
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"];
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
|
]["components"]["schemas"]["PlayableStreamingMode"];
|
||||||
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"];
|
||||||
|
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: {
|
_links: {
|
||||||
self: {
|
self: {
|
||||||
href: string;
|
href: string;
|
||||||
|
|
@ -1084,19 +1158,31 @@ export interface external {
|
||||||
endSequenceStartTime: string | null;
|
endSequenceStartTime: string | null;
|
||||||
duration: string | null;
|
duration: string | null;
|
||||||
/** @description List of mediaAssets */
|
/** @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 */
|
/** @description Livebuffer if element is live */
|
||||||
liveBuffer: Partial<
|
liveBuffer:
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LiveBufferVm"]
|
| Partial<
|
||||||
> | null;
|
external[
|
||||||
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
|
]["components"]["schemas"]["LiveBufferVm"]
|
||||||
|
>
|
||||||
|
| null;
|
||||||
/** @description List of subtitles */
|
/** @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: {
|
MediaUrlVm: {
|
||||||
/** @description The media URL itself. */
|
/** @description The media URL itself. */
|
||||||
url: string;
|
url: string;
|
||||||
format: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableFormat"];
|
format: external[
|
||||||
mimeType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableMimeType"];
|
"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 */
|
/** @description True if program is marked as HDSecure, false otherwise */
|
||||||
encrypted: boolean;
|
encrypted: boolean;
|
||||||
};
|
};
|
||||||
|
|
@ -1120,7 +1206,9 @@ export interface external {
|
||||||
bufferStartTime: string | null;
|
bufferStartTime: string | null;
|
||||||
/** @description Live buffer length */
|
/** @description Live buffer length */
|
||||||
bufferDuration: string;
|
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
|
* @description Type of liveBuffer
|
||||||
|
|
@ -1128,9 +1216,15 @@ export interface external {
|
||||||
*/
|
*/
|
||||||
LiveBufferType: "fixed" | "growing" | "none" | "sliding";
|
LiveBufferType: "fixed" | "growing" | "none" | "sliding";
|
||||||
SubtitlesVm: {
|
SubtitlesVm: {
|
||||||
type: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleType"];
|
type: external[
|
||||||
language: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleLanguage"];
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
label: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleLabel"];
|
]["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;
|
defaultOn: boolean;
|
||||||
webVtt: string;
|
webVtt: string;
|
||||||
};
|
};
|
||||||
|
|
@ -1139,7 +1233,10 @@ export interface external {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
SubtitleLanguage: "en" | "nb";
|
SubtitleLanguage: "en" | "nb";
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
SubtitleLabel: "English" | "Norsk tekst" | "Tekstet for hørselshemmede";
|
SubtitleLabel:
|
||||||
|
| "English"
|
||||||
|
| "Norsk tekst"
|
||||||
|
| "Tekstet for hørselshemmede";
|
||||||
ScoresStatisticsVm: {
|
ScoresStatisticsVm: {
|
||||||
springStreamSite: string;
|
springStreamSite: string;
|
||||||
springStreamStream: string;
|
springStreamStream: string;
|
||||||
|
|
@ -1178,14 +1275,18 @@ export interface external {
|
||||||
* When duration is unknown, eg channels: -1
|
* When duration is unknown, eg channels: -1
|
||||||
*/
|
*/
|
||||||
duration: number;
|
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
|
* @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-based streaming protocols, it should be the URL of the top-level
|
||||||
* manifest.
|
* manifest.
|
||||||
*/
|
*/
|
||||||
streamUrl: string;
|
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} */
|
/** @enum {string} */
|
||||||
ConvivaStreamType: "LIVE" | "VOD";
|
ConvivaStreamType: "LIVE" | "VOD";
|
||||||
|
|
@ -1221,7 +1322,9 @@ export interface external {
|
||||||
* Example: underholdning
|
* Example: underholdning
|
||||||
*/
|
*/
|
||||||
category: string;
|
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
|
* @description Name of selected CDN
|
||||||
* Example: Telenor-Cdn
|
* Example: Telenor-Cdn
|
||||||
|
|
@ -1239,7 +1342,9 @@ export interface external {
|
||||||
* Example: tv.nrk.no, nrk.no, nrksuper.no
|
* Example: tv.nrk.no, nrk.no, nrksuper.no
|
||||||
*/
|
*/
|
||||||
serviceName: string;
|
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
|
* @description MediaType for current content
|
||||||
|
|
@ -1257,8 +1362,12 @@ export interface external {
|
||||||
| "Podcast"
|
| "Podcast"
|
||||||
| "Program";
|
| "Program";
|
||||||
LunaStatisticsVm: {
|
LunaStatisticsVm: {
|
||||||
config: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaConfig"];
|
config: external[
|
||||||
data: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaPayload"];
|
"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: {
|
LunaConfig: {
|
||||||
beacon: string;
|
beacon: string;
|
||||||
|
|
@ -1288,27 +1397,37 @@ export interface external {
|
||||||
* @description Only applicable for on-demand media elements. Will be null for live media
|
* @description Only applicable for on-demand media elements. Will be null for live media
|
||||||
* elements.
|
* elements.
|
||||||
*/
|
*/
|
||||||
onDemand: Partial<
|
onDemand:
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["OnDemandUsageRights"]
|
| Partial<
|
||||||
> | null;
|
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
|
* @description Only applicable for live media elements. Will be null for on-demand media
|
||||||
* elements.
|
* elements.
|
||||||
*/
|
*/
|
||||||
live:
|
live:
|
||||||
| (Partial<{
|
| (
|
||||||
|
& Partial<{
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
type: "channel";
|
type: "channel";
|
||||||
/** @enum {boolean} */
|
/** @enum {boolean} */
|
||||||
isOngoing: true;
|
isOngoing: true;
|
||||||
transmissionInterval: unknown | null;
|
transmissionInterval: unknown | null;
|
||||||
}> &
|
}>
|
||||||
Partial<{
|
& Partial<{
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
type: "transmission";
|
type: "transmission";
|
||||||
isOngoing: boolean;
|
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;
|
| null;
|
||||||
externalEmbeddingAllowed: boolean;
|
externalEmbeddingAllowed: boolean;
|
||||||
};
|
};
|
||||||
|
|
@ -1326,8 +1445,12 @@ export interface external {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
PlayableSourceMedium: "audio" | "video";
|
PlayableSourceMedium: "audio" | "video";
|
||||||
NonPlayableElementVm: {
|
NonPlayableElementVm: {
|
||||||
reason: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableReason"];
|
reason: external[
|
||||||
messageType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableMessageType"];
|
"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
|
* @description Standard message for end user describing why the media element is not
|
||||||
* playable.
|
* playable.
|
||||||
|
|
@ -1389,7 +1512,9 @@ export interface external {
|
||||||
/** @description Image to show before play */
|
/** @description Image to show before play */
|
||||||
PosterVm: {
|
PosterVm: {
|
||||||
/** @description List of images in different sizes */
|
/** @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 */
|
/** @description Title to show before play */
|
||||||
TitlesVm: {
|
TitlesVm: {
|
||||||
|
|
@ -1397,12 +1522,18 @@ export interface external {
|
||||||
subtitle: string;
|
subtitle: string;
|
||||||
};
|
};
|
||||||
PreplayVm: {
|
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 Description to show before play */
|
||||||
description: string;
|
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 */
|
/** @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: {
|
LegalAgeRating: {
|
||||||
code: string;
|
code: string;
|
||||||
|
|
@ -1412,7 +1543,9 @@ export interface external {
|
||||||
LegalAgeBodyRated: {
|
LegalAgeBodyRated: {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
status: "rated";
|
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: {
|
LegalAgeBodyExempt: {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
|
|
@ -1420,11 +1553,16 @@ export interface external {
|
||||||
};
|
};
|
||||||
LegalAgeVm: {
|
LegalAgeVm: {
|
||||||
legalReference: string;
|
legalReference: string;
|
||||||
body: Partial<
|
body:
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeBodyExempt"]
|
& Partial<
|
||||||
> &
|
external[
|
||||||
Partial<
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeBodyRated"]
|
]["components"]["schemas"]["LegalAgeBodyExempt"]
|
||||||
|
>
|
||||||
|
& Partial<
|
||||||
|
external[
|
||||||
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
|
]["components"]["schemas"]["LegalAgeBodyRated"]
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
PlayableMetadataVm: {
|
PlayableMetadataVm: {
|
||||||
|
|
@ -1434,33 +1572,63 @@ export interface external {
|
||||||
PlayableMetadata: {
|
PlayableMetadata: {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
playability: "playable";
|
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;
|
nonPlayable: unknown | null;
|
||||||
id: string;
|
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;
|
duration: string | null;
|
||||||
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"];
|
availability: external[
|
||||||
legalAge: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"];
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
preplay: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PreplayVm"];
|
]["components"]["schemas"]["AvailabilityVm"];
|
||||||
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
|
legalAge: external[
|
||||||
skipDialogInfo: Partial<
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"]
|
]["components"]["schemas"]["LegalAgeVm"];
|
||||||
> | null;
|
preplay: external[
|
||||||
sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"];
|
"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: {
|
_links: {
|
||||||
self: {
|
self: {
|
||||||
href: string;
|
href: string;
|
||||||
};
|
};
|
||||||
manifests: Partial<
|
manifests:
|
||||||
|
| Partial<
|
||||||
{
|
{
|
||||||
href: string;
|
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<
|
| null;
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NextLink"]
|
next:
|
||||||
> | null;
|
| Partial<
|
||||||
series?: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SeriesLink"];
|
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?: {
|
progress?: {
|
||||||
href: string;
|
href: string;
|
||||||
/** @enum {boolean} */
|
/** @enum {boolean} */
|
||||||
|
|
@ -1468,50 +1636,90 @@ export interface external {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
_embedded: {
|
_embedded: {
|
||||||
manifests: Partial<
|
manifests:
|
||||||
|
| Partial<
|
||||||
{
|
{
|
||||||
id: string;
|
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: {
|
_links: {
|
||||||
self: {
|
self: {
|
||||||
href: string;
|
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: {
|
NonPlayableMetadata: {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
playability: "nonPlayable";
|
playability: "nonPlayable";
|
||||||
playable: unknown | null;
|
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;
|
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;
|
duration: string | null;
|
||||||
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"];
|
availability: external[
|
||||||
legalAge: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"];
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
preplay: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PreplayVm"];
|
]["components"]["schemas"]["AvailabilityVm"];
|
||||||
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"];
|
legalAge: external[
|
||||||
skipDialogInfo: Partial<
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"]
|
]["components"]["schemas"]["LegalAgeVm"];
|
||||||
> | null;
|
preplay: external[
|
||||||
sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"];
|
"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: {
|
_links: {
|
||||||
self: {
|
self: {
|
||||||
href: string;
|
href: string;
|
||||||
};
|
};
|
||||||
manifests: Partial<
|
manifests:
|
||||||
|
| Partial<
|
||||||
{
|
{
|
||||||
href: string;
|
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<
|
| null;
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NextLink"]
|
next:
|
||||||
> | null;
|
| Partial<
|
||||||
series?: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SeriesLink"];
|
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?: {
|
progress?: {
|
||||||
href: string;
|
href: string;
|
||||||
/** @enum {boolean} */
|
/** @enum {boolean} */
|
||||||
|
|
@ -1519,25 +1727,40 @@ export interface external {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
_embedded: {
|
_embedded: {
|
||||||
manifests: Partial<
|
manifests:
|
||||||
|
| Partial<
|
||||||
{
|
{
|
||||||
id: string;
|
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: {
|
_links: {
|
||||||
self: {
|
self: {
|
||||||
href: string;
|
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<
|
MetadataResponse:
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableMetadata"]
|
& Partial<
|
||||||
> &
|
external[
|
||||||
Partial<
|
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
|
||||||
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableMetadata"]
|
]["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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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 */
|
/** OK */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
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"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
71
lib/nrk.ts
71
lib/nrk.ts
|
|
@ -1,20 +1,21 @@
|
||||||
import { get, OK } from "https://deno.land/x/kall@v0.1.0/mod.ts";
|
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";
|
||||||
|
|
||||||
|
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
|
||||||
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never
|
export type Serie = catalogComponents["schemas"]["SeriesViewModel"];
|
||||||
export type Serie = catalogComponents["schemas"]["SeriesViewModel"]
|
|
||||||
type OriginalEpisode = catalogComponents["schemas"]["EpisodeHalResource"];
|
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 SearchResultList = searchComponents["schemas"]["seriesResult"]["results"];
|
||||||
export type SearchResult = ArrayElement<SearchResultList> & { description?: string }
|
export type SearchResult = ArrayElement<SearchResultList> & {
|
||||||
|
description?: string;
|
||||||
|
};
|
||||||
|
|
||||||
/* Incomplete manifest types */
|
/* Incomplete manifest types */
|
||||||
interface Manifest {
|
interface Manifest {
|
||||||
id: string;
|
id: string;
|
||||||
playable: {
|
playable: {
|
||||||
endSequenceStartTime: null,
|
endSequenceStartTime: null;
|
||||||
duration: string;
|
duration: string;
|
||||||
assets: {
|
assets: {
|
||||||
url: string;
|
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
|
// 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) {
|
if (playbackStatus === OK && playbackResponse) {
|
||||||
return { ...episode, url: playbackResponse.playable.assets[0].url };
|
return { ...episode, url: playbackResponse.playable.assets[0].url };
|
||||||
} else {
|
} 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 = {
|
export const nrkRadio = {
|
||||||
search: async (query: string): Promise<SearchResultList> => {
|
search: async (query: string): Promise<SearchResultList> => {
|
||||||
|
const [status, response] = await get<
|
||||||
const [status, response] = await get<searchComponents["schemas"]["searchresult"]>(`https://psapi.nrk.no/radio/search/search?q=${query}`);
|
searchComponents["schemas"]["searchresult"]
|
||||||
|
>(`https://psapi.nrk.no/radio/search/search?q=${query}`);
|
||||||
if (status === OK && response) {
|
if (status === OK && response) {
|
||||||
return response.results.series?.results;
|
return response.results.series?.results;
|
||||||
} else {
|
} else {
|
||||||
throw `Something went wrong with ${query} - got status ${status}`
|
throw `Something went wrong with ${query} - got status ${status}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getSerieData: async (seriesId: string) => {
|
getSerieData: async (seriesId: string) => {
|
||||||
|
|
||||||
const [
|
const [
|
||||||
[episodeStatus, episodeResponse],
|
[episodeStatus, episodeResponse],
|
||||||
[seriesStatus, serieResponse],
|
[seriesStatus, serieResponse],
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
get<catalogComponents["schemas"]["EpisodesHalResource"]>(`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes`),
|
get<catalogComponents["schemas"]["EpisodesHalResource"]>(
|
||||||
get<catalogComponents["schemas"]["SeriesHalResource"]>(`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}`)
|
`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes`,
|
||||||
|
),
|
||||||
|
get<catalogComponents["schemas"]["SeriesHalResource"]>(
|
||||||
|
`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}`,
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (
|
||||||
if (episodeStatus === OK && seriesStatus === OK && serieResponse?.series && episodeResponse?._embedded.episodes?.length) {
|
episodeStatus === OK && seriesStatus === OK &&
|
||||||
const episodes = await Promise.all(episodeResponse._embedded.episodes.map(episode => withDownloadLink(episode)));
|
serieResponse?.series && episodeResponse?._embedded.episodes?.length
|
||||||
|
) {
|
||||||
|
const episodes = await Promise.all(
|
||||||
|
episodeResponse._embedded.episodes.map((episode) => withDownloadLink(episode)),
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
...serieResponse.series,
|
...serieResponse.series,
|
||||||
episodes,
|
episodes,
|
||||||
}
|
};
|
||||||
} else {
|
} 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> => {
|
getEpisode: async (
|
||||||
|
seriesId: string,
|
||||||
|
episodeId: string,
|
||||||
const url = `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`
|
): Promise<OriginalEpisode> => {
|
||||||
|
const url = `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`;
|
||||||
const [status, episode] = await get<OriginalEpisode>(url);
|
const [status, episode] = await get<OriginalEpisode>(url);
|
||||||
if (status !== OK) {
|
if (status !== OK) {
|
||||||
throw `Error getting episode ${episodeId}. Status: ${status}. Series: ${seriesId}`;
|
throw `Error getting episode ${episodeId}. Status: ${status}. Series: ${seriesId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return episode;
|
return episode;
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -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 { 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";
|
import { getHostName } from "../../../utils.ts";
|
||||||
|
|
||||||
function toItemTag(seriesId: string, episode: Episode) {
|
function toItemTag(seriesId: string, episode: Episode) {
|
||||||
|
|
||||||
const description = episode.titles.subtitle || "";
|
const description = episode.titles.subtitle || "";
|
||||||
return tag("item", [
|
return tag("item", [
|
||||||
tag("title", episode.titles.title),
|
tag("title", episode.titles.title),
|
||||||
|
|
@ -16,7 +15,10 @@ function toItemTag(seriesId: string, episode: Episode) {
|
||||||
tag("pubDate", new Date(episode.date).toUTCString()),
|
tag("pubDate", new Date(episode.date).toUTCString()),
|
||||||
tag("itunes:duration", episode.durationInSeconds.toString()),
|
tag("itunes:duration", episode.durationInSeconds.toString()),
|
||||||
tag("podcast:chapters", "", [
|
tag("podcast:chapters", "", [
|
||||||
["url", `${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`],
|
[
|
||||||
|
"url",
|
||||||
|
`${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`,
|
||||||
|
],
|
||||||
["type", "application/json+chapters"],
|
["type", "application/json+chapters"],
|
||||||
]),
|
]),
|
||||||
tag("enclosure", "", [
|
tag("enclosure", "", [
|
||||||
|
|
@ -24,11 +26,11 @@ function toItemTag(seriesId: string, episode: Episode) {
|
||||||
["length", episode.durationInSeconds.toString()],
|
["length", episode.durationInSeconds.toString()],
|
||||||
["type", "audio/mpeg3"],
|
["type", "audio/mpeg3"],
|
||||||
]),
|
]),
|
||||||
])
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildFeed(seriesId: string) {
|
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 imageUrl = serie.squareImage.at(-1)?.url;
|
||||||
const linkValue = `https://radio.nrk.no/podkast/${serie.id}`;
|
const linkValue = `https://radio.nrk.no/podkast/${serie.id}`;
|
||||||
|
|
||||||
|
|
@ -51,18 +53,17 @@ async function buildFeed(seriesId: string) {
|
||||||
podcast feed validation.
|
podcast feed validation.
|
||||||
*/
|
*/
|
||||||
tag("itunes:category", "", [["text", "Government"]]),
|
tag("itunes:category", "", [["text", "Government"]]),
|
||||||
tag("itunes:owner",
|
tag("itunes:owner", [
|
||||||
[
|
|
||||||
tag("itunes:name", "NRK"),
|
tag("itunes:name", "NRK"),
|
||||||
tag("itunes:email", "nrkpodcast@nrk.no")
|
tag("itunes:email", "nrkpodcast@nrk.no"),
|
||||||
]
|
]),
|
||||||
),
|
|
||||||
tag(
|
tag(
|
||||||
"description",
|
"description",
|
||||||
serie.titles.subtitle || ""
|
serie.titles.subtitle || "",
|
||||||
),
|
),
|
||||||
tag("ttl", "60"), //60 minutes
|
tag("ttl", "60"), //60 minutes
|
||||||
...(imageUrl ? [
|
...(imageUrl
|
||||||
|
? [
|
||||||
tag("itunes:image", "", [
|
tag("itunes:image", "", [
|
||||||
["href", imageUrl],
|
["href", imageUrl],
|
||||||
]),
|
]),
|
||||||
|
|
@ -70,9 +71,10 @@ async function buildFeed(seriesId: string) {
|
||||||
tag("url", imageUrl),
|
tag("url", imageUrl),
|
||||||
tag("title", serie.titles.title),
|
tag("title", serie.titles.title),
|
||||||
tag("link", linkValue),
|
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:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd"],
|
||||||
["xmlns:content", "http://purl.org/rss/1.0/modules/content/"],
|
["xmlns:content", "http://purl.org/rss/1.0/modules/content/"],
|
||||||
["xmlns:podcast", "https://podcastindex.org/namespace/1.0"],
|
["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 seriesId = _ctx.params.seriesId;
|
||||||
const feedContent = await buildFeed(seriesId);
|
const feedContent = await buildFeed(seriesId);
|
||||||
|
|
||||||
return new Response(feedContent, {
|
return new Response(feedContent, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/xml"
|
"Content-Type": "application/xml",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,15 @@ import { parse, toSeconds } from "https://esm.sh/iso8601-duration@2.1.1";
|
||||||
import { nrkRadio, OriginalEpisode } from "../../../../../lib/nrk.ts";
|
import { nrkRadio, OriginalEpisode } from "../../../../../lib/nrk.ts";
|
||||||
|
|
||||||
function toChapters(episode: OriginalEpisode) {
|
function toChapters(episode: OriginalEpisode) {
|
||||||
return episode.indexPoints.map(indexPoint => ({
|
return episode.indexPoints.map((indexPoint) => ({
|
||||||
title: indexPoint.title,
|
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 seriesId = _ctx.params.seriesId;
|
||||||
const episodeId = _ctx.params.episodeId;
|
const episodeId = _ctx.params.episodeId;
|
||||||
|
|
||||||
|
|
@ -17,11 +20,11 @@ export const handler = async (req: Request, _ctx: HandlerContext): Promise<Respo
|
||||||
const body = {
|
const body = {
|
||||||
version: "1.2.0",
|
version: "1.2.0",
|
||||||
chapters,
|
chapters,
|
||||||
}
|
};
|
||||||
|
|
||||||
return new Response(JSON.stringify(body), {
|
return new Response(JSON.stringify(body), {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Head } from "$fresh/runtime.ts";
|
import { Head } from "$fresh/runtime.ts";
|
||||||
import { Handlers, PageProps } from "$fresh/server.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 { SerieCard } from "../components/SeriesCard.tsx";
|
||||||
import { nrkRadio, SearchResult, SearchResultList, Serie } from "../lib/nrk.ts";
|
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>
|
<h2 className="lg:text-xl">NRK-podcast som RSS</h2>
|
||||||
</div>
|
</div>
|
||||||
<Search defaultValue={data.query} />
|
<Search defaultValue={data.query} />
|
||||||
{data.result ? (
|
{data.result
|
||||||
|
? (
|
||||||
<div className="w-full mx-auto my-4">
|
<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>
|
</div>
|
||||||
) : null}
|
)
|
||||||
|
: null}
|
||||||
<h2 className="text-3xl">Hva er dette?</h2>
|
<h2 className="text-3xl">Hva er dette?</h2>
|
||||||
<p>Hei! Denne løsningen er laget som en reaksjon på 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 på 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>
|
<h2 className="text-3xl">Hvordan bruker jeg dette?</h2>
|
||||||
<p>Søk på NRK-podcasten du vil høre på. 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 på NRK-podcasten du vil høre på. 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>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue