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/ 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.

View File

@ -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>
); );
} }

View File

@ -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 (
@ -14,6 +14,6 @@ export function SerieCard(props: { serie: SearchResult, origin: string }) {
Kopier URL Kopier URL
</CopyButton> </CopyButton>
</div> </div>
</div > </div>
); );
} }

View File

@ -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"
} }
} }

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 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;

View File

@ -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

View File

@ -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"];
}; };
}; };
}; };

View File

@ -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;
} },
} };

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 { 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",
} },
}); });
}; };

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"; 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",
} },
}); });
}; };

View File

@ -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 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> <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> </div>
</> </>
); );

View File

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