diff --git a/README.md b/README.md index 49a2ce0..246a514 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,21 @@ -# NRSS - RSS Feeds for NRK sin podcaster +# NRSS - RSS feeds for NRK's podcasts -https://nrss.deno.dev/ +Live version: [nrss.deno.dev](https://nrss.deno.dev/) -## Local devlopment +A webapp built with Deno's [Fresh](https://fresh.deno.dev/) that generates +public accessible RSS-feeds for their produced podcasts via their +[API](https://psapi.nrk.no/documentation/). -install Deno -https://deno.land/manual/getting_started/installation +## Local development -### Run the application +1. [Install Deno](https://deno.land/manual/getting_started/installation) +1. Run the app: `deno task start` +1. Open [localhost:8000](http://localhost:8000) in your browser -```shell -deno task start -``` +## What is this? -Open http://localhost:8000/ to view it in the browser +This is made as a reaction that the goverment funded NRK is closing their own +content to their own app instead of building under open standards like RSS. ## Known Problems diff --git a/deno.json b/deno.json index 406fe81..e8e12b5 100644 --- a/deno.json +++ b/deno.json @@ -5,7 +5,8 @@ "author": " Olav Sundfør ", "tasks": { "start": "deno run -A --watch=static/,routes/ dev.ts", - "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx" + "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx", + "types:nrk": "deno run -A generate-types.ts && deno fmt ./lib" }, "imports": { "$fresh/": "https://deno.land/x/fresh@1.6.8/", @@ -14,7 +15,8 @@ "preact-render-to-string": "https://esm.sh/*preact-render-to-string@5.2.4", "@preact/signals": "https://esm.sh/*@preact/signals@1.2.2", "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1", - "$gfm": "https://deno.land/x/gfm@0.6.0/mod.ts" + "$gfm": "https://deno.land/x/gfm@0.6.0/mod.ts", + "openapi-typescript": "npm:openapi-typescript@6.7.5" }, "compilerOptions": { "jsx": "react-jsx", @@ -37,4 +39,4 @@ ] } } -} \ No newline at end of file +} diff --git a/generate-types.ts b/generate-types.ts new file mode 100644 index 0000000..c481263 --- /dev/null +++ b/generate-types.ts @@ -0,0 +1,28 @@ +import openapiTS from "openapi-typescript"; + +const nrkTypes: { filename: string; url: URL }[] = [ + { + filename: "./lib/nrk-search.ts", + url: new URL("https://psapi.nrk.no/documentation/openapi/search-radio/v1/openapi.yml"), + }, + { + filename: "./lib/nrk-catalog.ts", + url: new URL("https://psapi.nrk.no/documentation/openapi/programsider-radio/openapi.yml"), + }, + { + filename: "./lib/nrk-playback.ts", + url: new URL("https://psapi.nrk.no/documentation/openapi/playback/2.0/openapi.json"), + }, +] as const; + +async function generateTypes() { + for (const type of nrkTypes) { + const contents = await openapiTS(type.url); + + await Deno.writeTextFile(new URL(type.filename, import.meta.url), contents); + } +} + +if (import.meta.main) { + await generateTypes(); +} diff --git a/lib/nrk-catalog.ts b/lib/nrk-catalog.ts index 889a656..520e232 100644 --- a/lib/nrk-catalog.ts +++ b/lib/nrk-catalog.ts @@ -5,67 +5,114 @@ export interface paths { "/radio/catalog/episode/context/{episodeId}": { - /** Takes a episode id and returns the context. Supports ODM and podcast episodes. */ + /** + * Episode context + * @description Takes a episode id and returns the context. Supports ODM and podcast episodes. + */ get: operations["GetEpisodeContext"]; }; "/radio/catalog/programsContext/{programId}": { - /** Gets navigation aid for a programId. */ + /** + * Program context + * @description Gets navigation aid for a programId. + */ get: operations["GetProgramContext"]; }; "/radio/catalog/programs/{programId}": { - /** Gets the program page for a program id */ + /** + * Program + * @description Gets the program page for a program id + */ get: operations["GetProgram"]; }; "/radio/catalog/series/{seriesId}/type": { - /** Gets the radio series type */ + /** + * Series type + * @description Gets the radio series type + */ get: operations["GetSeriesType"]; }; "/radio/catalog/series/{seriesId}": { - /** Gets a radio series page */ + /** + * Series + * @description Gets a radio series page + */ get: operations["GetSeries"]; }; "/radio/catalog/series/{seriesId}/episodes": { - /** Gets episodes for a radio series */ + /** + * Series episodes + * @description Gets episodes for a radio series + */ get: operations["GetSeriesepisodes"]; }; "/radio/catalog/series/{seriesId}/seasons/{seasonId}": { - /** Gets a season for a radio series */ + /** + * Series season + * @description Gets a season for a radio series + */ get: operations["GetSeriesSeason"]; }; "/radio/catalog/series/{seriesId}/seasons/{seasonId}/episodes": { - /** Gets episodes for a radio series season */ + /** + * Series season episodes + * @description Gets episodes for a radio series season + */ get: operations["GetSeriesSeasonEpisodes"]; }; "/radio/catalog/extramaterial/{id}/clips": { - /** Gets clip extramaterials for a radio or podcast series */ + /** + * Series extramaterial clips + * @description Gets clip extramaterials for a radio or podcast series + */ get: operations["GetExtramaterialClips"]; }; "/radio/catalog/version/{id}": { - /** Gets the preferred series type (ODM or podcast) */ + /** + * Version + * @description Gets the preferred series type (ODM or podcast) + */ get: operations["GetVersion"]; }; "/radio/catalog/podcast/{podcastId}": { - /** Gets a podcast page */ + /** + * Podcast + * @description Gets a podcast page + */ get: operations["GetPodcast"]; }; "/radio/catalog/podcast/{podcastId}/episodes": { - /** Gets episodes for a podcast series */ + /** + * Podcast episodes + * @description Gets episodes for a podcast series + */ get: operations["GetPodcastepisodes"]; }; "/radio/catalog/podcast/{podcastId}/episodes/{podcastEpisodeId}": { - /** Gets a podcast episode */ + /** + * Podcast episode + * @description Gets a podcast episode + */ get: operations["GetPodcastEpisode"]; }; "/radio/catalog/podcast/{podcastId}/seasons/{seasonId}": { - /** Gets a season for a podcast series */ + /** + * Podcast season + * @description Gets a season for a podcast series + */ get: operations["GetPodcastSeason"]; }; "/radio/catalog/podcast/{podcastId}/seasons/{seasonId}/episodes": { - /** Gets episodes for a podcast series season */ + /** + * Podcast season episodes + * @description Gets episodes for a podcast series season + */ get: operations["GetPodcastSeasonEpisodes"]; }; } +export type webhooks = Record; + export interface components { schemas: { EpisodeContext: { @@ -119,8 +166,6 @@ export interface components { badges?: components["schemas"]["Badge"][]; /** @description Number of new episodes in this season. Used in conjunction with `progressesForNewEpisodes`-link to check progresses for new episodes in season. New-badge should be removed if the number of new episodes matches the number of episodes with started/finished progress. Only applicable for seasons in Umbrella-podcasts. Otherwise not in response. */ newEpisodesCount?: number; - } & { - links: unknown; }; SeasonSeriesHalEmbeddedLinks: { self: components["schemas"]["HalLink"]; @@ -168,6 +213,9 @@ export interface components { date: string; titles: components["schemas"]["Titles"]; originalTitle?: string; + /** @description Video clip ID for podcast episode. */ + clipId?: string; + category?: components["schemas"]["Category"]; /** @description The array is empty if no images are present */ image?: components["schemas"]["Image"][]; /** @description Duration of the episode. We use the [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) format for duration. */ @@ -178,7 +226,6 @@ export interface components { /** Format: int32 */ productionYear?: number; availability: components["schemas"]["AvailabilityVm"]; - contributors?: components["schemas"]["ContributorVm"][]; /** @description Array of badges indication statuses */ badges?: components["schemas"]["Badge"][]; }; @@ -258,6 +305,7 @@ export interface components { /** @description The array is empty if no images are present */ image: components["schemas"]["Image"][]; titles: components["schemas"]["Titles"]; + temporalTitles: components["schemas"]["TemporalTitles"]; availability: components["schemas"]["AvailabilityVm"]; category: components["schemas"]["OdmCategory"]; usageRights?: components["schemas"]["UsageRightsVm"]; @@ -275,11 +323,14 @@ export interface components { date: string; category: components["schemas"]["Category"]; programInformation: components["schemas"]["ProgramInformation"]; + /** @description Video clip ID for podcast episode. */ + clipId?: string; /** @description The array is empty if no images are present */ image: components["schemas"]["Image"][]; titles: components["schemas"]["Titles"]; /** @description Podcast indexpoints use the same contract as odm index points but only Title and StartPoint will be set */ indexPoints?: components["schemas"]["IndexPoint"][]; + contributors?: components["schemas"]["ContributorVm"][]; availability: components["schemas"]["AvailabilityVm"]; usageRights?: components["schemas"]["UsageRightsVm"]; duration: components["schemas"]["Duration"]; @@ -342,6 +393,27 @@ export interface components { title: string; subtitle?: string | null; }; + /** @description Table of titles and when they should be displayed. The array of titles will in many cases be empty, but the default title is always present */ + TemporalTitles: { + titles: components["schemas"]["TemporalTitle"][]; + defaultTitles: { + /** @description The series title */ + mainTitle: string; + /** @description The program title */ + subtitle: string; + }; + }; + /** @description List of titles. May be empty */ + TemporalTitle: { + from: string; + to: string; + titles: { + /** @description The series title */ + mainTitle: string; + /** @description The program title */ + subtitle: string; + }; + }; UsageRightsVm: { from: components["schemas"]["UsageRightsDateVm"]; to: components["schemas"]["UsageRightsDateVm"]; @@ -356,12 +428,7 @@ export interface components { displayValue: components["schemas"]["GeoBlockDisplayValue"]; }; /** @enum {string} */ - AvailabilityStatus: - | "coming" - | "available" - | "expires" - | "expired" - | "notAvailableOnline"; + AvailabilityStatus: "coming" | "available" | "expires" | "expired" | "notAvailableOnline"; /** * @example { * "href": "/some/url" @@ -507,26 +574,22 @@ export interface components { }; /** @description Aggregated info for clips */ ExtraMaterialClipsHalEmbedded: { - metadata?: { - displayAspectRatio: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DisplayAspectRatio"]; + metadata?: ({ + displayAspectRatio: external["../playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; /** * @description Clip length. The string is formatted as https://en.wikipedia.org/wiki/ISO_8601#Durations * @example PT2M0S */ duration: string; id: string; - /** @enum {undefined} */ + /** @enum {unknown} */ playability: "nonPlayable" | "playable"; - preplay: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PreplayVm"]; + preplay: external["../playback/openapi.json"]["components"]["schemas"]["PreplayVm"]; /** @description Link to full metadata */ _links: { self: components["schemas"]["HalLink"]; }; - }[]; + })[]; }; ExtraMaterialClipsHalLinks: { self: components["schemas"]["HalLink"]; @@ -534,414 +597,70 @@ export interface components { }; Badge: { label?: string; - /** @enum {undefined} */ + /** @enum {unknown} */ type?: "new"; }; }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; } -export interface operations { - /** Takes a episode id and returns the context. Supports ODM and podcast episodes. */ - GetEpisodeContext: { - parameters: { - path: { - /** The episode id */ - episodeId: string; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["EpisodeContext"]; - }; - }; - /** Episode not found */ - 404: unknown; - }; - }; - /** Gets navigation aid for a programId. */ - GetProgramContext: { - parameters: { - path: { - /** The program id */ - programId: string; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["EpisodeContext"]; - }; - }; - /** Program not found */ - 404: unknown; - }; - }; - /** Gets the program page for a program id */ - GetProgram: { - parameters: { - path: { - /** The program id */ - programId: string; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["OdmProgramsHalResource"]; - }; - }; - /** Program not found */ - 404: unknown; - }; - }; - /** Gets the radio series type */ - GetSeriesType: { - parameters: { - path: { - /** The series id */ - seriesId: string; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["SeriesTypeHalResource"]; - }; - }; - /** Series not found */ - 404: unknown; - }; - }; - /** Gets a radio series page */ - GetSeries: { - parameters: { - path: { - /** The series id */ - seriesId: string; - }; - query: { - /** This parameter only affects Standard and News Series. Default is 10. */ - pageSize?: number; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["SeriesHalResource"]; - }; - }; - /** Series not found */ - 404: unknown; - }; - }; - /** Gets episodes for a radio series */ - GetSeriesepisodes: { - parameters: { - path: { - /** The series id */ - seriesId: string; - }; - query: { - /** Number of episodes returned. Default is 10 */ - pageSize?: number; - /** Which page to take from */ - page?: number; - /** Sorts episodes by date (Only applies for standard series episodes) */ - sort?: components["schemas"]["SortDirection"]; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["EpisodesHalResource"]; - }; - }; - /** Series not found */ - 404: unknown; - }; - }; - /** Gets a season for a radio series */ - GetSeriesSeason: { - parameters: { - path: { - /** The series id */ - seriesId: string; - /** The season id */ - seasonId: string; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["SeasonHalResource"]; - }; - }; - /** Series or season not found */ - 404: unknown; - }; - }; - /** Gets episodes for a radio series season */ - GetSeriesSeasonEpisodes: { - parameters: { - path: { - /** The series id */ - seriesId: string; - /** The season id */ - seasonId: string; - }; - query: { - /** Number of episodes returned. Default is 10 */ - pageSize?: number; - /** Which page to take from */ - page?: number; - /** Sorts episodes by date (only applies for standard series episodes) */ - sort?: components["schemas"]["SortDirection"]; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["EpisodesHalResource"]; - }; - }; - /** Series or season not found */ - 404: unknown; - }; - }; - /** Gets clip extramaterials for a radio or podcast series */ - GetExtramaterialClips: { - parameters: { - path: { - /** The series or podcast id */ - id: string; - }; - query: { - /** Number of clips returned. Default is 10 */ - pageSize?: number; - /** Which page to take from. Default is 1 */ - page?: number; - /** Sorts videos by date. Default is 'desc' */ - sort?: components["schemas"]["SortDirection"]; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["ExtraMaterialClipsHalResource"]; - }; - }; - /** No podcast or series with id found */ - 404: unknown; - }; - }; - /** Gets the preferred series type (ODM or podcast) */ - GetVersion: { - parameters: { - path: { - /** The series or podcast id */ - id: string; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["SeriesHalResource"]; - }; - }; - /** Not found */ - 404: unknown; - }; - }; - /** Gets a podcast page */ - GetPodcast: { - parameters: { - path: { - /** The podcast id */ - podcastId: string; - }; - query: { - /** Default is 10. */ - pageSize?: number; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["SeriesHalResource"]; - }; - }; - /** Podcast not found */ - 404: unknown; - }; - }; - /** Gets episodes for a podcast series */ - GetPodcastepisodes: { - parameters: { - path: { - /** The podcast id */ - podcastId: string; - }; - query: { - /** Number of episodes returned. Default is 10 */ - pageSize?: number; - /** Which page to take from */ - page?: number; - /** Sorts episodes by date (only applies for standard series episodes) */ - sort?: components["schemas"]["SortDirection"]; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["EpisodesHalResource"]; - }; - }; - /** Podcast not found */ - 404: unknown; - }; - }; - /** Gets a podcast episode */ - GetPodcastEpisode: { - parameters: { - path: { - /** The podcast id */ - podcastId: string; - /** The podcast episode id */ - podcastEpisodeId: string; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["PodcastEpisodeHalResource"]; - }; - }; - /** Podcast or episode not found */ - 404: unknown; - }; - }; - /** Gets a season for a podcast series */ - GetPodcastSeason: { - parameters: { - path: { - /** The podcast id */ - podcastId: string; - /** The season id */ - seasonId: string; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["PodcastSeasonHalResource"]; - }; - }; - /** Podcast or season not found */ - 404: unknown; - }; - }; - /** Gets episodes for a podcast series season */ - GetPodcastSeasonEpisodes: { - parameters: { - path: { - /** The podcast id */ - podcastId: string; - /** The season id */ - seasonId: string; - }; - query: { - /** Number of episodes returned. Default is 10 */ - pageSize?: number; - /** Which page to take from */ - page?: number; - /** Sorts episodes by date (only applies for standard series episodes) */ - sort?: components["schemas"]["SortDirection"]; - }; - }; - responses: { - /** Success */ - 200: { - content: { - "application/json": components["schemas"]["EpisodesHalResource"]; - }; - }; - /** Podcast or season not found */ - 404: unknown; - }; - }; -} +export type $defs = Record; export interface external { - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json": { + "../playback/openapi.json": { paths: { "/playback/debug": { - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackDebug"]; + /** Debug info for detected client and client-ip */ + get: operations["getPlaybackDebug"]; }; "/playback/dnslist": { - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackDnsList"]; + /** List all DNS-aliases used for streaming. */ + get: operations["getPlaybackDnsList"]; }; "/playback/manifest/{id}": { - /** This endpoint is just for clients that doesn't know its context */ - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackManifestRedirect"]; + /** + * Playback Manifest for the specified program, clip or channel. + * @description This endpoint is just for clients that doesn't know its context + */ + get: operations["getPlaybackManifestRedirect"]; }; "/playback/manifest/program/{programId}": { - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackProgramManifest"]; + /** Playback Manifest for the specified program. */ + get: operations["getPlaybackProgramManifest"]; }; "/playback/manifest/channel/{channelId}": { - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackChannelManifest"]; + /** Playback Manifest for the specified channel. */ + get: operations["getPlaybackChannelManifest"]; }; "/playback/manifest/clip/{clipId}": { - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackClipManifest"]; + /** Playback Manifest for the specified clip. */ + get: operations["getPlaybackClipManifest"]; }; "/playback/metadata/{id}": { - /** This endpoint is just for clients that doesn't know its context */ - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackMetadataRedirect"]; + /** + * Playback Metadata for the specified program, clip or channel. + * @description This endpoint is just for clients that doesn't know its context + */ + get: operations["getPlaybackMetadataRedirect"]; }; "/playback/metadata/program/{programId}": { - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackProgramMetadata"]; + /** Playback Metadata for the specified program. */ + get: operations["getPlaybackProgramMetadata"]; }; "/playback/metadata/channel/{channelId}": { - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackChannelMetadata"]; + /** Playback Metadata for the specified channel. */ + get: operations["getPlaybackChannelMetadata"]; }; "/playback/metadata/clip/{clipId}": { - get: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["operations"]["getPlaybackClipMetadata"]; + /** Playback Metadata for the specified clip . */ + get: operations["getPlaybackClipMetadata"]; }; }; + webhooks: Record; components: { schemas: { DnsList: { @@ -959,12 +678,8 @@ export interface external { href: string; }; }; - playbackData: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlaybackDataVm"]; - locationData: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LocationDataVm"]; + playbackData: external["../playback/openapi.json"]["components"]["schemas"]["PlaybackDataVm"]; + locationData: external["../playback/openapi.json"]["components"]["schemas"]["LocationDataVm"]; }; PlaybackDataVm: { maxBitrate: number; @@ -979,15 +694,9 @@ export interface external { isBeta: boolean; originalUa: string; wurflId: string | null; - distributionFormat: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DistributionFormat"]; - deviceGroupName: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DeviceGroupName"]; - playerPlatform: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayerPlatform"]; + distributionFormat: external["../playback/openapi.json"]["components"]["schemas"]["DistributionFormat"]; + deviceGroupName: external["../playback/openapi.json"]["components"]["schemas"]["DeviceGroupName"]; + playerPlatform: external["../playback/openapi.json"]["components"]["schemas"]["PlayerPlatform"]; customManifestParameters: ( | "AudioOnly" | "DolbyOnly" @@ -999,24 +708,12 @@ export interface external { | "NoSignalDiscontinuities" | "SignalDiscontinuities" )[]; - subtitlesDistribution: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SubtitlesDistribution"]; + subtitlesDistribution: external["../playback/openapi.json"]["components"]["schemas"]["SubtitlesDistribution"]; }; /** @enum {string} */ - DistributionFormat: - | "Dash" - | "Hds" - | "Hls" - | "ProgressiveDownload" - | "ProgressiveMp3"; + DistributionFormat: "Dash" | "Hds" | "Hls" | "ProgressiveDownload" | "ProgressiveMp3"; /** @enum {string} */ - DeviceGroupName: - | "Desktop" - | "Mobile" - | "SetTopUnit" - | "Tablet" - | "Tv"; + DeviceGroupName: "Desktop" | "Mobile" | "SetTopUnit" | "Tablet" | "Tv"; /** @enum {string} */ PlayerPlatform: | "Altibox" @@ -1063,50 +760,24 @@ export interface external { realIp: string; }; ManifestResponse: - & Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableManifest"] - > - & Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["NonPlayableManifest"] - >; + | external["../playback/openapi.json"]["components"]["schemas"]["PlayableManifest"] + | external["../playback/openapi.json"]["components"]["schemas"]["NonPlayableManifest"]; PlayableManifest: { /** @enum {string} */ playability: "playable"; - playable: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableElementVm"]; + playable: external["../playback/openapi.json"]["components"]["schemas"]["PlayableElementVm"]; statistics: { - scores: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ScoresStatisticsVm"]; - ga: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["GoogleAnalyticsVm"]; - conviva: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ConvivaStatisticsVm"]; - luna: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LunaStatisticsVm"]; + scores: external["../playback/openapi.json"]["components"]["schemas"]["ScoresStatisticsVm"]; + ga: external["../playback/openapi.json"]["components"]["schemas"]["GoogleAnalyticsVm"]; + conviva: external["../playback/openapi.json"]["components"]["schemas"]["ConvivaStatisticsVm"]; + luna: external["../playback/openapi.json"]["components"]["schemas"]["LunaStatisticsVm"]; }; - nonPlayable: unknown | null; + nonPlayable: unknown; id: string; - streamingMode: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableStreamingMode"]; - availability: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["AvailabilityVm"]; - displayAspectRatio: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DisplayAspectRatio"]; - sourceMedium: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableSourceMedium"]; + streamingMode: external["../playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["../playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; + displayAspectRatio: external["../playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; + sourceMedium: external["../playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; _links: { self: { href: string; @@ -1121,28 +792,18 @@ export interface external { NonPlayableManifest: { /** @enum {string} */ playability: "nonPlayable"; - playable: unknown | null; + playable: unknown; statistics: { - scores: unknown | null; - ga: unknown | null; - luna: unknown | null; + scores: unknown; + ga: unknown; + luna: unknown; }; - nonPlayable: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["NonPlayableElementVm"]; + nonPlayable: external["../playback/openapi.json"]["components"]["schemas"]["NonPlayableElementVm"]; id: string; - streamingMode: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableStreamingMode"]; - availability: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["AvailabilityVm"]; - displayAspectRatio: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DisplayAspectRatio"]; - sourceMedium: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableSourceMedium"]; + streamingMode: external["../playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["../playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; + displayAspectRatio: external["../playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; + sourceMedium: external["../playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; _links: { self: { href: string; @@ -1158,31 +819,17 @@ export interface external { endSequenceStartTime: string | null; duration: string | null; /** @description List of mediaAssets */ - assets: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["MediaUrlVm"][]; + assets: external["../playback/openapi.json"]["components"]["schemas"]["MediaUrlVm"][]; /** @description Livebuffer if element is live */ - liveBuffer: - | Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LiveBufferVm"] - > - | null; + liveBuffer: external["../playback/openapi.json"]["components"]["schemas"]["LiveBufferVm"] | null; /** @description List of subtitles */ - subtitles: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SubtitlesVm"][]; + subtitles: external["../playback/openapi.json"]["components"]["schemas"]["SubtitlesVm"][]; }; MediaUrlVm: { /** @description The media URL itself. */ url: string; - format: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableFormat"]; - mimeType: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableMimeType"]; + format: external["../playback/openapi.json"]["components"]["schemas"]["PlayableFormat"]; + mimeType: external["../playback/openapi.json"]["components"]["schemas"]["PlayableMimeType"]; /** @description True if program is marked as HDSecure, false otherwise */ encrypted: boolean; }; @@ -1206,9 +853,7 @@ export interface external { bufferStartTime: string | null; /** @description Live buffer length */ bufferDuration: string; - bufferType: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LiveBufferType"]; + bufferType: external["../playback/openapi.json"]["components"]["schemas"]["LiveBufferType"]; }; /** * @description Type of liveBuffer @@ -1216,15 +861,9 @@ export interface external { */ LiveBufferType: "fixed" | "growing" | "none" | "sliding"; SubtitlesVm: { - type: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SubtitleType"]; - language: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SubtitleLanguage"]; - label: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SubtitleLabel"]; + type: external["../playback/openapi.json"]["components"]["schemas"]["SubtitleType"]; + language: external["../playback/openapi.json"]["components"]["schemas"]["SubtitleLanguage"]; + label: external["../playback/openapi.json"]["components"]["schemas"]["SubtitleLabel"]; defaultOn: boolean; webVtt: string; }; @@ -1233,10 +872,7 @@ export interface external { /** @enum {string} */ SubtitleLanguage: "en" | "nb"; /** @enum {string} */ - SubtitleLabel: - | "English" - | "Norsk tekst" - | "Tekstet for hørselshemmede"; + SubtitleLabel: "English" | "Norsk tekst" | "Tekstet for hørselshemmede"; ScoresStatisticsVm: { springStreamSite: string; springStreamStream: string; @@ -1258,7 +894,9 @@ export interface external { * * dimension29: Platform * [Ekstern doc](https://github.com/nrkno/analytics-docs/blob/master/docs/schema.md) */ - GoogleAnalyticsVm: { [key: string]: string }; + GoogleAnalyticsVm: { + [key: string]: string; + }; ConvivaStatisticsVm: { /** * @description A string identifying the current video application. @@ -1275,18 +913,14 @@ export interface external { * When duration is unknown, eg channels: -1 */ duration: number; - streamType: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ConvivaStreamType"]; + streamType: external["../playback/openapi.json"]["components"]["schemas"]["ConvivaStreamType"]; /** * @description The URL from where this video content will be downloaded. For * manifest-based streaming protocols, it should be the URL of the top-level * manifest. */ streamUrl: string; - custom: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ConvivaCustomProperties"]; + custom: external["../playback/openapi.json"]["components"]["schemas"]["ConvivaCustomProperties"]; }; /** @enum {string} */ ConvivaStreamType: "LIVE" | "VOD"; @@ -1322,9 +956,7 @@ export interface external { * Example: underholdning */ category: string; - mediaType: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ConvivaMediaType"]; + mediaType: external["../playback/openapi.json"]["components"]["schemas"]["ConvivaMediaType"]; /** * @description Name of selected CDN * Example: Telenor-Cdn @@ -1342,9 +974,7 @@ export interface external { * Example: tv.nrk.no, nrk.no, nrksuper.no */ serviceName: string; - contentType: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ConvivaContentType"]; + contentType: external["../playback/openapi.json"]["components"]["schemas"]["ConvivaContentType"]; }; /** * @description MediaType for current content @@ -1355,19 +985,10 @@ export interface external { * @description Type of content * @enum {string} */ - ConvivaContentType: - | "Channel" - | "Clip" - | "Event" - | "Podcast" - | "Program"; + ConvivaContentType: "Channel" | "Clip" | "Event" | "Podcast" | "Program"; LunaStatisticsVm: { - config: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LunaConfig"]; - data: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LunaPayload"]; + config: external["../playback/openapi.json"]["components"]["schemas"]["LunaConfig"]; + data: external["../playback/openapi.json"]["components"]["schemas"]["LunaPayload"]; }; LunaConfig: { beacon: string; @@ -1397,37 +1018,25 @@ export interface external { * @description Only applicable for on-demand media elements. Will be null for live media * elements. */ - onDemand: - | Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["OnDemandUsageRights"] - > - | null; + onDemand: external["../playback/openapi.json"]["components"]["schemas"]["OnDemandUsageRights"] | null; /** * @description Only applicable for live media elements. Will be null for on-demand media * elements. */ live: - | ( - & Partial<{ - /** @enum {string} */ - type: "channel"; - /** @enum {boolean} */ - isOngoing: true; - transmissionInterval: unknown | null; - }> - & Partial<{ - /** @enum {string} */ - type: "transmission"; - isOngoing: boolean; - transmissionInterval: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"][ - "LiveTransmissionIntervalVm" - ]; - }> - ) + | ({ + /** @enum {string} */ + type: "channel"; + /** @enum {boolean} */ + isOngoing: true; + transmissionInterval: unknown; + } | { + /** @enum {string} */ + type: "transmission"; + isOngoing: boolean; + transmissionInterval: + external["../playback/openapi.json"]["components"]["schemas"]["LiveTransmissionIntervalVm"]; + }) | null; externalEmbeddingAllowed: boolean; }; @@ -1445,12 +1054,8 @@ export interface external { /** @enum {string} */ PlayableSourceMedium: "audio" | "video"; NonPlayableElementVm: { - reason: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["NonPlayableReason"]; - messageType: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["NonPlayableMessageType"]; + reason: external["../playback/openapi.json"]["components"]["schemas"]["NonPlayableReason"]; + messageType: external["../playback/openapi.json"]["components"]["schemas"]["NonPlayableMessageType"]; /** * @description Standard message for end user describing why the media element is not * playable. @@ -1512,9 +1117,7 @@ export interface external { /** @description Image to show before play */ PosterVm: { /** @description List of images in different sizes */ - images: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PosterImageVm"][]; + images: external["../playback/openapi.json"]["components"]["schemas"]["PosterImageVm"][]; }; /** @description Title to show before play */ TitlesVm: { @@ -1522,18 +1125,12 @@ export interface external { subtitle: string; }; PreplayVm: { - titles: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["TitlesVm"]; + titles: external["../playback/openapi.json"]["components"]["schemas"]["TitlesVm"]; /** @description Description to show before play */ description: string; - poster: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PosterVm"]; + poster: external["../playback/openapi.json"]["components"]["schemas"]["PosterVm"]; /** @description List of index points */ - indexPoints: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["IndexPointVm"][]; + indexPoints: external["../playback/openapi.json"]["components"]["schemas"]["IndexPointVm"][]; }; LegalAgeRating: { code: string; @@ -1543,9 +1140,7 @@ export interface external { LegalAgeBodyRated: { /** @enum {string} */ status: "rated"; - rating: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LegalAgeRating"]; + rating: external["../playback/openapi.json"]["components"]["schemas"]["LegalAgeRating"]; }; LegalAgeBodyExempt: { /** @enum {string} */ @@ -1554,16 +1149,8 @@ export interface external { LegalAgeVm: { legalReference: string; body: - & Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LegalAgeBodyExempt"] - > - & Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LegalAgeBodyRated"] - >; + | external["../playback/openapi.json"]["components"]["schemas"]["LegalAgeBodyExempt"] + | external["../playback/openapi.json"]["components"]["schemas"]["LegalAgeBodyRated"]; }; PlayableMetadataVm: { /** @description Uri to resolve to get the media manifest */ @@ -1572,63 +1159,27 @@ export interface external { PlayableMetadata: { /** @enum {string} */ playability: "playable"; - playable: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableMetadataVm"]; - nonPlayable: unknown | null; + playable: external["../playback/openapi.json"]["components"]["schemas"]["PlayableMetadataVm"]; + nonPlayable: unknown; id: string; - streamingMode: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableStreamingMode"]; + streamingMode: external["../playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"]; duration: string | null; - availability: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["AvailabilityVm"]; - legalAge: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LegalAgeVm"]; - preplay: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PreplayVm"]; - displayAspectRatio: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DisplayAspectRatio"]; - skipDialogInfo: - | Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SkipDialogInfoVm"] - > - | null; - sourceMedium: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableSourceMedium"]; + availability: external["../playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; + legalAge: external["../playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"]; + preplay: external["../playback/openapi.json"]["components"]["schemas"]["PreplayVm"]; + displayAspectRatio: external["../playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; + skipDialogInfo: external["../playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"] | null; + sourceMedium: external["../playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; _links: { self: { href: string; }; - manifests: - | Partial< - { - href: string; - name: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"][ - "ManifestLinkName" - ]; - }[] - > - | null; - next: - | Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["NextLink"] - > - | null; - series?: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SeriesLink"]; + manifests: { + href: string; + name: external["../playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + next: external["../playback/openapi.json"]["components"]["schemas"]["NextLink"] | null; + series?: external["../playback/openapi.json"]["components"]["schemas"]["SeriesLink"]; progress?: { href: string; /** @enum {boolean} */ @@ -1636,90 +1187,42 @@ export interface external { }; }; _embedded: { - manifests: - | Partial< - { - id: string; - availabilityLabel: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"][ - "AvailabilityLabel" - ]; - _links: { - self: { - href: string; - name: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"][ - "ManifestLinkName" - ]; - }; - }; - }[] - > - | null; + manifests: { + id: string; + availabilityLabel: external["../playback/openapi.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + self: { + href: string; + name: external["../playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }[] | null; }; }; NonPlayableMetadata: { /** @enum {string} */ playability: "nonPlayable"; - playable: unknown | null; - nonPlayable: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["NonPlayableElementVm"]; + playable: unknown; + nonPlayable: external["../playback/openapi.json"]["components"]["schemas"]["NonPlayableElementVm"]; id: string; - streamingMode: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableStreamingMode"]; + streamingMode: external["../playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"]; duration: string | null; - availability: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["AvailabilityVm"]; - legalAge: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["LegalAgeVm"]; - preplay: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PreplayVm"]; - displayAspectRatio: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DisplayAspectRatio"]; - skipDialogInfo: - | Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SkipDialogInfoVm"] - > - | null; - sourceMedium: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableSourceMedium"]; + availability: external["../playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; + legalAge: external["../playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"]; + preplay: external["../playback/openapi.json"]["components"]["schemas"]["PreplayVm"]; + displayAspectRatio: external["../playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; + skipDialogInfo: external["../playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"] | null; + sourceMedium: external["../playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; _links: { self: { href: string; }; - manifests: - | Partial< - { - href: string; - name: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"][ - "ManifestLinkName" - ]; - }[] - > - | null; - next: - | Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["NextLink"] - > - | null; - series?: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["SeriesLink"]; + manifests: { + href: string; + name: external["../playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + next: external["../playback/openapi.json"]["components"]["schemas"]["NextLink"] | null; + series?: external["../playback/openapi.json"]["components"]["schemas"]["SeriesLink"]; progress?: { href: string; /** @enum {boolean} */ @@ -1727,296 +1230,695 @@ export interface external { }; }; _embedded: { - manifests: - | Partial< - { - id: string; - availabilityLabel: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"][ - "AvailabilityLabel" - ]; - _links: { - self: { - href: string; - name: external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"][ - "ManifestLinkName" - ]; - }; - }; - }[] - > - | null; + manifests: { + id: string; + availabilityLabel: external["../playback/openapi.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + self: { + href: string; + name: external["../playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }[] | null; }; }; MetadataResponse: - & Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["PlayableMetadata"] - > - & Partial< - external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["NonPlayableMetadata"] - >; + | external["../playback/openapi.json"]["components"]["schemas"]["PlayableMetadata"] + | external["../playback/openapi.json"]["components"]["schemas"]["NonPlayableMetadata"]; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; + }; + $defs: Record; + }; +} + +export interface operations { + /** + * Episode context + * @description Takes a episode id and returns the context. Supports ODM and podcast episodes. + */ + GetEpisodeContext: { + parameters: { + path: { + /** @description The episode id */ + episodeId: string; }; }; - operations: { - getPlaybackDebug: { - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DebugData"]; - }; - }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["EpisodeContext"]; }; }; - getPlaybackDnsList: { - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["DnsList"]; - }; - }; + /** @description Episode not found */ + 404: { + content: never; + }; + }; + }; + /** + * Program context + * @description Gets navigation aid for a programId. + */ + GetProgramContext: { + parameters: { + path: { + /** @description The program id */ + programId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["EpisodeContext"]; }; }; - /** This endpoint is just for clients that doesn't know its context */ - getPlaybackManifestRedirect: { - parameters: { - path: { - /** - * The unique ID for the playback element. - * Different kinds of media elements have different kinds of IDs. - * - On-demand (PRF): Four letters and eight digits. Example: MSUB19120216 - * - Klipp (Potion / Foss2): GUID. - * - Klipp (Foss1): The letter `f` followed by a sequence of digits. - * - Klipp (Guri): A sequence of digits. - * - Podcast: Prefix "nrkno-poddkast-". Example: nrkno-poddkast-10908-146680-20122018123000 - * - Kanal: The name of the channel. Example: nrk1 - */ - id: string; - }; - query: { - /** Is the client in in Super-universe or not. Used to get correct statistics and content */ - inSuperUniverse?: string; - /** PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. Legal values: low, mid, high, auto */ - preferredBandwidth?: string; - /** PreferredCdn could be used to get the CDN used in the last request. Overrides Cedexis. The value that should be passed in exists as a header, named: CdnName */ - preferredCdn?: string; - /** Use to activate liveToVod on playback */ - live2Vod?: string; - }; - }; - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ManifestResponse"]; - }; - }; + /** @description Program not found */ + 404: { + content: never; + }; + }; + }; + /** + * Program + * @description Gets the program page for a program id + */ + GetProgram: { + parameters: { + path: { + /** @description The program id */ + programId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["OdmProgramsHalResource"]; }; }; - getPlaybackProgramManifest: { - parameters: { - path: { - /** - * The unique ID for the playback element. - * Different kinds of media elements have different kinds of IDs. - * - On-demand (PRF): Four letters and eight digits. Example: MSUB19120216 - */ - programId: string; - }; - query: { - /** Is the client in in Super-universe or not. Used to get correct statistics and content */ - inSuperUniverse?: string; - /** PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. Legal values: low, mid, high, auto */ - preferredBandwidth?: string; - /** PreferredCdn could be used to get the CDN used in the last request. Overrides Cedexis. The value that should be passed in exists as a header, named: CdnName */ - preferredCdn?: string; - /** Used to request HLS manifest for offline. This overrides/ignores preferredBandwith. Tablet/phone information is pulled from User-Agent */ - offline?: "standard" | "high"; - }; - }; - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ManifestResponse"]; - }; - }; + /** @description Program not found */ + 404: { + content: never; + }; + }; + }; + /** + * Series type + * @description Gets the radio series type + */ + GetSeriesType: { + parameters: { + path: { + /** @description The series id */ + seriesId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["SeriesTypeHalResource"]; }; }; - getPlaybackChannelManifest: { - parameters: { - path: { - /** - * The unique ID for the playback element. - * Different kinds of media elements have different kinds of IDs. - * - Kanal: The name of the channel. Example: nrk1 - */ - channelId: string; - }; - query: { - /** Is the client in in Super-universe or not. Used to get correct statistics and content */ - inSuperUniverse?: string; - /** PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. Legal values: low, mid, high, auto */ - preferredBandwidth?: string; - /** PreferredCdn could be used to get the CDN used in the last request. Overrides Cedexis. The value that should be passed in exists as a header, named: CdnName */ - preferredCdn?: string; - }; - }; - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ManifestResponse"]; - }; - }; + /** @description Series not found */ + 404: { + content: never; + }; + }; + }; + /** + * Series + * @description Gets a radio series page + */ + GetSeries: { + parameters: { + query?: { + /** @description This parameter only affects Standard and News Series. Default is 10. */ + pageSize?: number; + }; + path: { + /** @description The series id */ + seriesId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["SeriesHalResource"]; }; }; - getPlaybackClipManifest: { - parameters: { - path: { - /** - * The unique ID for the playback element. - * Different kinds of media elements have different kinds of IDs. - * - Klipp (Potion / Foss2): GUID. - * - Klipp (Foss1): The letter `f` followed by a sequence of digits. - * - Klipp (Guri): A sequence of digits. - */ - clipId: string; - }; - query: { - /** PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. Legal values: low, mid, high, auto */ - preferredBandwidth?: string; - /** PreferredCdn could be used to get the CDN used in the last request. Overrides Cedexis. The value that should be passed in exists as a header, named: CdnName */ - preferredCdn?: string; - }; - }; - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["ManifestResponse"]; - }; - }; + /** @description Series not found */ + 404: { + content: never; + }; + }; + }; + /** + * Series episodes + * @description Gets episodes for a radio series + */ + GetSeriesepisodes: { + parameters: { + query?: { + /** @description Number of episodes returned. Default is 10 */ + pageSize?: number; + /** @description Which page to take from */ + page?: number; + /** @description Sorts episodes by date (Only applies for standard series episodes) */ + sort?: components["schemas"]["SortDirection"]; + }; + path: { + /** @description The series id */ + seriesId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["EpisodesHalResource"]; }; }; - /** This endpoint is just for clients that doesn't know its context */ - getPlaybackMetadataRedirect: { - parameters: { - path: { - /** - * The unique ID for the playback element. - * Different kinds of media elements have different kinds of IDs. - * - On-demand (PRF): Four letters and eight digits. Example: MSUB19120216 - * - Klipp (Potion / Foss2): GUID. - * - Klipp (Foss1): The letter `f` followed by a sequence of digits. - * - Klipp (Guri): A sequence of digits. - * - Podcast: Prefix "nrkno-poddkast-". Example: nrkno-poddkast-10908-146680-20122018123000 - * - Kanal: The name of the channel. Example: nrk1 - */ - id: string; - }; - query: { - /** Use to activate liveToVod on playback */ - live2Vod?: string; - }; - }; - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["MetadataResponse"]; - }; - }; + /** @description Series not found */ + 404: { + content: never; + }; + }; + }; + /** + * Series season + * @description Gets a season for a radio series + */ + GetSeriesSeason: { + parameters: { + path: { + /** @description The series id */ + seriesId: string; + /** @description The season id */ + seasonId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["SeasonHalResource"]; }; }; - getPlaybackProgramMetadata: { - parameters: { - path: { - /** - * The unique ID for the playback element. - * Different kinds of media elements have different kinds of IDs. - * - On-demand (PRF): Four letters and eight digits. Example: MSUB19120216 - */ - programId: string; - }; - }; - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["MetadataResponse"]; - }; - }; + /** @description Series or season not found */ + 404: { + content: never; + }; + }; + }; + /** + * Series season episodes + * @description Gets episodes for a radio series season + */ + GetSeriesSeasonEpisodes: { + parameters: { + query?: { + /** @description Number of episodes returned. Default is 10 */ + pageSize?: number; + /** @description Which page to take from */ + page?: number; + /** @description Sorts episodes by date (only applies for standard series episodes) */ + sort?: components["schemas"]["SortDirection"]; + }; + path: { + /** @description The series id */ + seriesId: string; + /** @description The season id */ + seasonId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["EpisodesHalResource"]; }; }; - getPlaybackChannelMetadata: { - parameters: { - path: { - /** - * The unique ID for the playback element. - * Different kinds of media elements have different kinds of IDs. - * - Kanal: The name of the channel. Example: nrk1 - */ - channelId: string; - }; - }; - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["MetadataResponse"]; - }; - }; + /** @description Series or season not found */ + 404: { + content: never; + }; + }; + }; + /** + * Series extramaterial clips + * @description Gets clip extramaterials for a radio or podcast series + */ + GetExtramaterialClips: { + parameters: { + query?: { + /** @description Number of clips returned. Default is 10 */ + pageSize?: number; + /** @description Which page to take from. Default is 1 */ + page?: number; + /** @description Sorts videos by date. Default is 'desc' */ + sort?: components["schemas"]["SortDirection"]; + }; + path: { + /** @description The series or podcast id */ + id: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["ExtraMaterialClipsHalResource"]; }; }; - getPlaybackClipMetadata: { - parameters: { - path: { - /** - * The unique ID for the playback element. - * Different kinds of media elements have different kinds of IDs. - * - Klipp (Potion / Foss2): GUID. - * - Klipp (Foss1): The letter `f` followed by a sequence of digits. - * - Klipp (Guri): A sequence of digits. - */ - clipId: string; - }; + /** @description No podcast or series with id found */ + 404: { + content: never; + }; + }; + }; + /** + * Version + * @description Gets the preferred series type (ODM or podcast) + */ + GetVersion: { + parameters: { + path: { + /** @description The series or podcast id */ + id: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["SeriesHalResource"]; }; - responses: { - /** OK */ - 200: { - content: { - "application/json": external[ - "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" - ]["components"]["schemas"]["MetadataResponse"]; - }; - }; + }; + /** @description Not found */ + 404: { + content: never; + }; + }; + }; + /** + * Podcast + * @description Gets a podcast page + */ + GetPodcast: { + parameters: { + query?: { + /** @description Default is 10. */ + pageSize?: number; + }; + path: { + /** @description The podcast id */ + podcastId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["SeriesHalResource"]; + }; + }; + /** @description Podcast not found */ + 404: { + content: never; + }; + }; + }; + /** + * Podcast episodes + * @description Gets episodes for a podcast series + */ + GetPodcastepisodes: { + parameters: { + query?: { + /** @description Number of episodes returned. Default is 10 */ + pageSize?: number; + /** @description Which page to take from */ + page?: number; + /** @description Sorts episodes by date (only applies for standard series episodes) */ + sort?: components["schemas"]["SortDirection"]; + }; + path: { + /** @description The podcast id */ + podcastId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["EpisodesHalResource"]; + }; + }; + /** @description Podcast not found */ + 404: { + content: never; + }; + }; + }; + /** + * Podcast episode + * @description Gets a podcast episode + */ + GetPodcastEpisode: { + parameters: { + path: { + /** @description The podcast id */ + podcastId: string; + /** @description The podcast episode id */ + podcastEpisodeId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["PodcastEpisodeHalResource"]; + }; + }; + /** @description Podcast or episode not found */ + 404: { + content: never; + }; + }; + }; + /** + * Podcast season + * @description Gets a season for a podcast series + */ + GetPodcastSeason: { + parameters: { + path: { + /** @description The podcast id */ + podcastId: string; + /** @description The season id */ + seasonId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["PodcastSeasonHalResource"]; + }; + }; + /** @description Podcast or season not found */ + 404: { + content: never; + }; + }; + }; + /** + * Podcast season episodes + * @description Gets episodes for a podcast series season + */ + GetPodcastSeasonEpisodes: { + parameters: { + query?: { + /** @description Number of episodes returned. Default is 10 */ + pageSize?: number; + /** @description Which page to take from */ + page?: number; + /** @description Sorts episodes by date (only applies for standard series episodes) */ + sort?: components["schemas"]["SortDirection"]; + }; + path: { + /** @description The podcast id */ + podcastId: string; + /** @description The season id */ + seasonId: string; + }; + }; + responses: { + /** @description Success */ + 200: { + content: { + "application/json": components["schemas"]["EpisodesHalResource"]; + }; + }; + /** @description Podcast or season not found */ + 404: { + content: never; + }; + }; + }; + /** Debug info for detected client and client-ip */ + getPlaybackDebug: { + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["DebugData"]; + }; + }; + }; + }; + /** List all DNS-aliases used for streaming. */ + getPlaybackDnsList: { + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["DnsList"]; + }; + }; + }; + }; + /** + * Playback Manifest for the specified program, clip or channel. + * @description This endpoint is just for clients that doesn't know its context + */ + getPlaybackManifestRedirect: { + parameters: { + query?: { + /** @description Is the client in in Super-universe or not. Used to get correct statistics and content */ + inSuperUniverse?: string; + /** @description PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. Legal values: low, mid, high, auto */ + preferredBandwidth?: string; + /** @description PreferredCdn could be used to get the CDN used in the last request. Overrides Cedexis. The value that should be passed in exists as a header, named: CdnName */ + preferredCdn?: string; + /** @description Use to activate liveToVod on playback */ + live2Vod?: string; + }; + path: { + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + * - On-demand (PRF): Four letters and eight digits. Example: MSUB19120216 + * - Klipp (Potion / Foss2): GUID. + * - Klipp (Foss1): The letter `f` followed by a sequence of digits. + * - Klipp (Guri): A sequence of digits. + * - Podcast: Prefix "nrkno-poddkast-". Example: nrkno-poddkast-10908-146680-20122018123000 + * - Kanal: The name of the channel. Example: nrk1 + */ + id: string; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["ManifestResponse"]; + }; + }; + }; + }; + /** Playback Manifest for the specified program. */ + getPlaybackProgramManifest: { + parameters: { + query?: { + /** @description Is the client in in Super-universe or not. Used to get correct statistics and content */ + inSuperUniverse?: string; + /** @description PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. Legal values: low, mid, high, auto */ + preferredBandwidth?: string; + /** @description PreferredCdn could be used to get the CDN used in the last request. Overrides Cedexis. The value that should be passed in exists as a header, named: CdnName */ + preferredCdn?: string; + /** @description Used to request HLS manifest for offline. This overrides/ignores preferredBandwith. Tablet/phone information is pulled from User-Agent */ + offline?: "standard" | "high"; + }; + path: { + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + * - On-demand (PRF): Four letters and eight digits. Example: MSUB19120216 + */ + programId: string; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["ManifestResponse"]; + }; + }; + }; + }; + /** Playback Manifest for the specified channel. */ + getPlaybackChannelManifest: { + parameters: { + query?: { + /** @description Is the client in in Super-universe or not. Used to get correct statistics and content */ + inSuperUniverse?: string; + /** @description PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. Legal values: low, mid, high, auto */ + preferredBandwidth?: string; + /** @description PreferredCdn could be used to get the CDN used in the last request. Overrides Cedexis. The value that should be passed in exists as a header, named: CdnName */ + preferredCdn?: string; + }; + path: { + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + * - Kanal: The name of the channel. Example: nrk1 + */ + channelId: string; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["ManifestResponse"]; + }; + }; + }; + }; + /** Playback Manifest for the specified clip. */ + getPlaybackClipManifest: { + parameters: { + query?: { + /** @description PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. Legal values: low, mid, high, auto */ + preferredBandwidth?: string; + /** @description PreferredCdn could be used to get the CDN used in the last request. Overrides Cedexis. The value that should be passed in exists as a header, named: CdnName */ + preferredCdn?: string; + }; + path: { + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + * - Klipp (Potion / Foss2): GUID. + * - Klipp (Foss1): The letter `f` followed by a sequence of digits. + * - Klipp (Guri): A sequence of digits. + */ + clipId: string; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["ManifestResponse"]; + }; + }; + }; + }; + /** + * Playback Metadata for the specified program, clip or channel. + * @description This endpoint is just for clients that doesn't know its context + */ + getPlaybackMetadataRedirect: { + parameters: { + query?: { + /** @description Use to activate liveToVod on playback */ + live2Vod?: string; + }; + path: { + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + * - On-demand (PRF): Four letters and eight digits. Example: MSUB19120216 + * - Klipp (Potion / Foss2): GUID. + * - Klipp (Foss1): The letter `f` followed by a sequence of digits. + * - Klipp (Guri): A sequence of digits. + * - Podcast: Prefix "nrkno-poddkast-". Example: nrkno-poddkast-10908-146680-20122018123000 + * - Kanal: The name of the channel. Example: nrk1 + */ + id: string; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["MetadataResponse"]; + }; + }; + }; + }; + /** Playback Metadata for the specified program. */ + getPlaybackProgramMetadata: { + parameters: { + path: { + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + * - On-demand (PRF): Four letters and eight digits. Example: MSUB19120216 + */ + programId: string; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["MetadataResponse"]; + }; + }; + }; + }; + /** Playback Metadata for the specified channel. */ + getPlaybackChannelMetadata: { + parameters: { + path: { + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + * - Kanal: The name of the channel. Example: nrk1 + */ + channelId: string; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["MetadataResponse"]; + }; + }; + }; + }; + /** Playback Metadata for the specified clip . */ + getPlaybackClipMetadata: { + parameters: { + path: { + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + * - Klipp (Potion / Foss2): GUID. + * - Klipp (Foss1): The letter `f` followed by a sequence of digits. + * - Klipp (Guri): A sequence of digits. + */ + clipId: string; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": external["../playback/openapi.json"]["components"]["schemas"]["MetadataResponse"]; }; }; }; diff --git a/lib/nrk-playback.ts b/lib/nrk-playback.ts new file mode 100644 index 0000000..931609c --- /dev/null +++ b/lib/nrk-playback.ts @@ -0,0 +1,3819 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/playback/manifest/{id}": { + /** + * Redirect to the appropriate manifest endpoint based on ID. + * @description This endpoint is for clients that don't know what kind of media they're going to play. It should only be used if absolutely necessary. The endpoint makes an effort to redirect the client to the appropriate playback endpoint based on pattern matching on the ID. This presumes that the domains of IDs for programs, clips, podcasts and channels are disjunct (which they happen to be). + */ + get: operations["getPlaybackManifestRedirect"]; + }; + "/playback/manifest/program/{programId}": { + /** Playback Manifest for the specified program. */ + get: operations["getPlaybackProgramManifest"]; + }; + "/playback/manifest/podcast/{podcastEpisodeId}": { + /** Playback Manifest for the specified podcast. */ + get: operations["getPlaybackPodcastManifest"]; + }; + "/playback/manifest/podcast/{podcastSeriesId}/{podcastEpisodeId}": { + /** Playback Manifest for the specified podcast episode. */ + get: operations["getPlaybackPodcastManifestWithSeriesId"]; + }; + "/playback/manifest/channel/{channelId}": { + /** + * Playback Manifest for the specified channel. + * @description [SLO](https://nrkconfluence.atlassian.net/l/c/GdzLGDxy) + */ + get: operations["getPlaybackChannelManifest"]; + }; + "/playback/manifest/clip/{clipId}": { + /** Playback Manifest for the specified clip. */ + get: operations["getPlaybackClipManifest"]; + }; + "/playback/manifest/radioclip/{radioClipType}/{radioClipId}": { + /** Playback Manifest for the specified radio clip. */ + get: operations["getPlaybackNewsClipManifest"]; + }; + "/playback/metadata/{id}": { + /** + * Redirect to the appropriate metadata endpoint based on ID. + * @description This endpoint is for clients that don't know what kind of media they're going to play. It should only be used if absolutely necessary. The endpoint makes an effort to redirect the client to the appropriate playback endpoint based on pattern matching on the ID. This presumes that the domains of IDs for programs, clips, podcasts and channels are disjunct (which they happen to be). + */ + get: operations["getPlaybackMetadataRedirect"]; + }; + "/playback/metadata/program/{programId}": { + /** + * Playback Metadata for the specified program. + * @description Live-to-VOD: For programs that are not yet playable but has an upcoming live-to-vod transmission and a live-to-vod asset, we link to up-to-date scheduling information by means of the 'schedule' link relation in the '_links' property. + */ + get: operations["getPlaybackProgramMetadata"]; + }; + "/playback/metadata/podcast/{podcastEpisodeId}": { + /** Podcast Metadata for the specified podcast episode. */ + get: operations["getPlaybackPodcastMetadata"]; + }; + "/playback/metadata/podcast/{podcastSeriesId}/{podcastEpisodeId}": { + /** Podcast Metadata for the specified podcast episode. */ + get: operations["getPlaybackPodcastMetadataWithSeries"]; + }; + "/playback/metadata/channel/{channelId}": { + /** + * Playback Metadata for the specified channel. + * @description [SLO](https://nrkconfluence.atlassian.net/l/c/GdzLGDxy) + */ + get: operations["getPlaybackChannelMetadata"]; + }; + "/playback/metadata/clip/{clipId}": { + /** Playback Metadata for the specified clip . */ + get: operations["getPlaybackClipMetadata"]; + }; + "/playback/metadata/radioclip/{radioClipType}/{radioClipId}": { + /** Playback Metadata for the specified radio clip. */ + get: operations["getPlaybackNewsClipMetadata"]; + }; + "/playback/schedule/program/{programId}": { + /** + * Schedule information for the specified live-to-vod program. + * @description Polling resource for accurate timing of playback of the live-to-vod recording for the specified program. The purpose is to provide a light-weight response that clients can use while waiting for a live-to-vod stream to be become available for playback. Hence the client will primarily want to check the 'playableNow' flag. Secondarily, if the program isn't yet playable, the 'playableInSeconds' property will indicate when the program is scheduled to be playable. The response is cacheable (sets max-age header). + */ + get: operations["getPlaybackSchedule"]; + }; + "/tv/live/plugs": { + /** + * PlugList of linear TV channels. + * @description PlugList of linear TV channels, with links to relevant resources. [SLO](https://nrkconfluence.atlassian.net/l/c/GdzLGDxy) + */ + get: operations["getTvLivePlugs"]; + }; + "/tv/live": { + /** + * Overview of linear TV channels. + * @description List of linear TV channels, with links to relevant resources. [SLO](https://nrkconfluence.atlassian.net/l/c/GdzLGDxy) + */ + get: operations["getTvLive"]; + }; + "/radio/live": { + /** + * Overview of linear radio channels. + * @description List of linear radio channels, with links to relevant resources. (Perhaps /radio/linear/channels would have been a better name.) + */ + get: operations["getRadioLive"]; + }; + "/playback/statistics/nielsen/channel/{channelId}": { + /** + * Nielsen statistics for the specified radio channel. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + get: operations["getNielsenStatisticsForChannel"]; + }; + "/playback/statistics/nielsen/podcast/{podcastEpisodeId}": { + /** + * Nielsen statistics for the specified podcast. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + get: operations["getNielsenStatisticsForPodcast"]; + }; + "/playback/statistics/nielsen/podcast/{podcastSeriesId}/{podcastEpisodeId}": { + /** + * Nielsen statistics for the specified podcast episode. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + get: operations["getNielsenStatisticsForPodcastWithSeriesId"]; + }; + "/playback/statistics/nielsen/program/{programId}": { + /** + * Nielsen statistics for the specified on-demand radio program. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + get: operations["getNielsenStatisticsForProgram"]; + }; + "/playback/statistics/nielsen/clip/{clipId}": { + /** + * Nielsen statistics for the specified audio clip. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + get: operations["getNielsenStatisticsForClip"]; + }; +} + +export type webhooks = Record; + +export interface components { + schemas: never; + responses: never; + parameters: { + /** @description The unique ID for the channel. */ + channelIdParam: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelId"]; + /** @description The unique ID for the clip. */ + clipIdParam: external["schemas/playback-clip.json"]["components"]["schemas"]["ClipId"]; + /** @description The unique ID for the playback element. */ + programIdParam: external["schemas/playback-program.json"]["components"]["schemas"]["ProgId"]; + /** @description The unique ID for the playback element. */ + programIdManifestParam: external["schemas/playback-program.json"]["components"]["schemas"]["ManifestProgId"]; + /** + * @description The unique ID for the podcast element. + * @example l_502f0a9b-e3e6-42aa-af0a-9be3e672aadb + */ + podcastEpisodeIdParam: external["schemas/playback-podcast.json"]["components"]["schemas"]["PodcastEpisodeId"]; + /** + * @description The unique ID for the podcast series. + * @example paalogga + */ + podcastSeriesIdParam: external["schemas/playback-podcast.json"]["components"]["schemas"]["PodcastSeriesId"]; + /** @description The parameter will add metadata for streamingChannels to the response. */ + showStreamingChannelsParam?: boolean; + /** @description Filter content based on appropriateness for given contentGroup. Affects playability for channels and programs. */ + contentGroupParam?: "children" | "adults"; + /** + * @description First item in list will be district channel based on regionId if contentGroup is 'adults' or not set + * @example NO-30 + */ + regionIdParam?: string; + /** @description Use to enable EEA portability on playback */ + "eea-portabilityParam"?: boolean; + /** + * @description The type of the radio clip. + * + * - news + */ + radioClipTypeParam: external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipType"]; + /** + * @description The unique ID for the playback element. + * - News clip : GUID. + */ + radioClipIdParam: string; + /** @description Clients should use this to indicate that they have implemented support for offline. */ + offlineParam?: "standard" | "high"; + /** @description Parental control age limit set on user profile. If omitted, no age check is performed. Matched against legalAge. */ + ageRestrictionParam?: "6" | "9" | "12" | "15" | "18" | "None"; + /** @description Use to indicate liveToVod support */ + live2vodParam?: boolean; + /** + * @description The unique ID for the playback element. + * Different kinds of media elements have different kinds of IDs. + */ + idParam: string; + /** @description PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. */ + preferredBandwidthParam?: "low" | "mid" | "high" | "auto"; + /** @description PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. highest returns a single quality, the best available for requesting client. Usecase: short videos like trailers. */ + preferredBandwidthProgramParam?: "low" | "mid" | "high" | "highest" | "auto"; + /** @description PreferredBandwidth could be used to limit the number of qualities in the HLS manifest. */ + preferredBandwidthAllParam?: "low" | "mid" | "high" | "highest" | "auto"; + /** @description PreferredCdn could be used to get the CDN used in the last request. Overrides CdnSelector. The value that should be passed in exists as a header, named: CdnName */ + preferredCdnParam?: string; + /** @description Is the client in in Super-universe or not. Used to get correct statistics and content */ + inSuperUniverseParam?: string; + /** @description subtitleType=all-speech will return stream url to stream with burnin subtitles on all speech. */ + subtitleTypeParam?: "all-speech"; + /** @description Based on client detection, set to 'hlg' if device can decode h265 with HLG transfer function AND display supports HDR. API may then return link to HDR/HLG stream. */ + hdrlevelParam?: "hlg"; + }; + requestBodies: never; + headers: never; + pathItems: never; +} + +export type $defs = Record; + +export interface external { + "examples/ChannelOverviewResponse/radio-live.json": unknown; + "examples/ChannelOverviewResponse/tv-live-plugs.json": unknown; + "examples/ChannelOverviewResponse/tv-live.json": unknown; + "examples/PlaybackManifestResponse/channel_nrk1.json": unknown; + "examples/PlaybackManifestResponse/clip_39b725c9-d2ed-470c-a2f1-58924a811848.json": unknown; + "examples/PlaybackManifestResponse/newsclip_442667f6-d6cf-3bdb-7603-cf938eb97944.json": unknown; + "examples/PlaybackManifestResponse/podcast_l_11ec3698-bcfd-4c37-ac36-98bcfdac37ff.json": unknown; + "examples/PlaybackManifestResponse/program_MSUB19120216.json": unknown; + "examples/PlaybackMetadataResponse/channel_nrk1.json": unknown; + "examples/PlaybackMetadataResponse/clip_39b725c9-d2ed-470c-a2f1-58924a811848.json": unknown; + "examples/PlaybackMetadataResponse/newsclip_442667f6-d6cf-3bdb-7603-cf938eb97944.json": unknown; + "examples/PlaybackMetadataResponse/podcast_l_11ec3698-bcfd-4c37-ac36-98bcfdac37ff.json": unknown; + "examples/PlaybackMetadataResponse/program_MSUB19120216.json": unknown; + "examples/ProgramScheduleResource/NNFA17071019.json": unknown; + "schemas/playback-channel.json": { + paths: Record; + webhooks: Record; + components: { + schemas: { + /** @example nrk1 */ + ChannelId: string; + PlaybackManifestResponse: + | external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableManifest"] + | external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayableManifest"]; + PlayableManifest: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableElement"]; + statistics: { + playability: "playable"; + scores: external["schemas/playback-channel.json"]["components"]["schemas"]["ScoresStatistics"]; + ga: external["schemas/playback-channel.json"]["components"]["schemas"]["GoogleAnalytics"]; + conviva: external["schemas/playback-channel.json"]["components"]["schemas"]["ConvivaStatistics"]; + luna: external["schemas/playback-channel.json"]["components"]["schemas"]["LunaStatistics"]; + qualityOfExperience: external["schemas/playback-channel.json"]["components"]["schemas"]["QoEStatistics"]; + snowplow: external["schemas/playback-channel.json"]["components"]["schemas"]["SnowPlow"]; + }; + nonPlayable: null; + id: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelId"]; + streamingMode: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["schemas/playback-channel.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: + | external["schemas/playback-channel.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-channel.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + sourceMedium: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + metadata: { + playability: "playable"; + href?: string; + /** @enum {string} */ + name?: "metadata"; + }; + }; + }; + NonPlayableManifest: { + /** @enum {string} */ + playability: "nonPlayable"; + /** @description This is always null when the channel is non-playable. */ + playable: null; + statistics: { + playability: "nonPlayable"; + /** @description Always null for nonPlayable */ + scores: null; + /** @description Always null for nonPlayable */ + ga: null; + /** @description Always null for nonPlayable */ + luna: null; + /** @description Always null for nonPlayable */ + qualityOfExperience: null; + /** @description Always null for nonPlayable */ + snowplow: null; + }; + nonPlayable: external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayableElement"]; + id: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelId"]; + streamingMode: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["schemas/playback-channel.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: + | external["schemas/playback-channel.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-channel.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + sourceMedium: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + }; + metadata: { + playability: "nonPlayable"; + href: string; + /** @enum {string} */ + name: "metadata"; + }; + nonPlayablePosterLinks?: + external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayablePosterLinkCollection"]; + }; + }; + /** @description Information that only applies to a playable channel. */ + PlayableElement: { + endSequenceStartTime: string | null; + /** @description Always null since channels neither begin nor end. */ + duration: null; + /** @description List of media assets. Currently only one item will be provided (in HLS format) but in the future this list might be expanded to contain different types of assets. */ + assets: external["schemas/playback-channel.json"]["components"]["schemas"]["MediaUrl"][]; + liveBuffer: external["schemas/playback-channel.json"]["components"]["schemas"]["LiveBuffer"]; + /** @description List of thumbnails. */ + thumbnails: external["schemas/playback-channel.json"]["components"]["schemas"]["ThumbnailsItem"][]; + /** @description List of subtitles for clients that do side-loading. Subtitles are in the [.vtt format](https://en.wikipedia.org/wiki/WebVTT). This is also true for subtitles linked in the manifest files. Currently, we don't offer subtitles for channels, so this array is empty. This may change in the future. */ + subtitles: external["schemas/playback-channel.json"]["components"]["schemas"]["Subtitles"][]; + }; + MediaUrl: { + /** @description Link to the media manifest. */ + url: string; + format: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableFormat"]; + mimeType: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableMimeType"]; + /** @description True if program is marked as HDSecure, false otherwise. */ + encrypted: boolean; + }; + /** + * @description The video format for the resource identified by the URL. + * @enum {string} + */ + PlayableFormat: "HLS" | "MP4" | "MP3"; + /** + * @description The mimetype that corresponds to the specified format. + * @enum {string} + */ + PlayableMimeType: "application/mp4" | "application/vnd.apple.mpegurl" | "audio/mp4" | "video/mp4" | "audio/mp3"; + /** @description Information about the channel's live buffer. */ + LiveBuffer: { + /** @description Always null, since the live buffer for a channel is a sliding buffer. */ + bufferStartTime: null; + /** + * Format: ISO8601-duration + * @description The length of the live buffer as an ISO 8601 formatted string. + * @example PT3H + */ + bufferDuration: string; + /** + * @description Indicates the kind of live buffer. Channels have a sliding live buffer. + * @enum {string} + */ + bufferType: "sliding"; + }; + /** @description Thumbnails item. The thumbnails follow the displayAspectRatio for the channel, which can be used to derive height from width. Only relevant for video channels, null otherwise. */ + ThumbnailsItem: { + /** @description Url to thumbnails feed in .vtt format. */ + url: string; + /** @description Width of the thumbnail. */ + width: number; + }; + Subtitles: { + type: external["schemas/playback-channel.json"]["components"]["schemas"]["SubtitleType"]; + language: external["schemas/playback-channel.json"]["components"]["schemas"]["SubtitleLanguage"]; + label: external["schemas/playback-channel.json"]["components"]["schemas"]["SubtitleLabel"]; + defaultOn: boolean; + webVtt: string; + }; + /** @enum {string} */ + SubtitleType: "gbr" | "mix" | "nor" | "ttv"; + /** @enum {string} */ + SubtitleLanguage: "en" | "nb"; + /** @enum {string} */ + SubtitleLabel: "English" | "Norsk" | "Tekstet for hørselshemmede" | "Norsk – på all tale"; + /** @description This payload should be submitted as-is to Scores. */ + ScoresStatistics: { + /** + * @description Possible values are 'nrkstream' and 'nrkradio' for production, 'test' for other environments. + * @example nrkstream + */ + springStreamSite: string; + /** + * @description A structured string describing the content. + * Example: 'programspiller/live/nrk_p3'. + * @example programspiller/live/nrk_p3 + */ + springStreamStream: string; + /** + * @description A structured string describing the client. + * Examples: 'desktop', 'mobil/app/android', 'tv/smart_tv/samsung' etc. + * @example tv/smart_tv/samsung + */ + springStreamContentType: string; + /** @description For channels, this is the empty string. */ + springStreamProgramId: string; + /** @description Always null since channels neither begin nor end. */ + springStreamDuration: null; + } | null; + /** + * @description * dimension1: ID. KILDESYSTEM:UNIK_IDENTIFIKATOR + * * dimension2: Tittel + * * dimension3: Publiseringsår. + * * dimension4: Publiseringsmåned. + * * dimension5: Publiseringsdag. [01, 31] + * * dimension10: Kilde-ID + * * dimension21: Serie. Kun relevant for episoder/instalments. + * * dimension22: Episodenummer. Kun relevant for episoder (instalments?). + * * dimension23: Kategori + * * dimension25: Mediatype [video | audio] + * * dimension26: Mediakategori [live | ondemand] + * * dimension29: Platform + * + * [Ekstern doc](https://github.com/nrkno/analytics-docs/blob/master/docs/schema.md) + */ + GoogleAnalytics: { + [key: string]: string; + }; + /** @description DEPRECATED! Should be null! */ + ConvivaStatistics: { + /** @enum {string} */ + playerName?: ""; + /** @enum {string} */ + assetName?: ""; + /** @enum {number} */ + duration?: -1; + /** @enum {string} */ + streamType?: ""; + /** @enum {string} */ + streamUrl?: ""; + custom?: external["schemas/playback-channel.json"]["components"]["schemas"]["ConvivaCustomProperties"]; + } | null; + /** @description Only allow empty string since Conviva is not in use. */ + ConvivaCustomProperties: { + /** @enum {string} */ + contentId: ""; + /** @enum {string} */ + series: ""; + /** @enum {string} */ + title: ""; + /** @enum {string} */ + episode: ""; + /** @enum {string} */ + category: ""; + /** @enum {string} */ + mediaType: ""; + /** @enum {string} */ + cdnName: ""; + /** @enum {string} */ + applicationVersion: ""; + /** @enum {string} */ + applicationName: ""; + /** @enum {string} */ + serviceName: ""; + /** @enum {string} */ + contentType: ""; + }; + QoEStatistics: { + /** @description DeviceGroup and Name detected by API */ + clientName: string; + /** @description CdnName for playable URL */ + cdnName: string; + /** @description StreamingFormat string representation */ + streamingFormat: string; + /** @description SegmentLength string representation */ + segmentLength: string; + /** + * @description Asset type, calculated based on API response + * @enum {string} + */ + assetType: "live"; + /** @description Request correlation Id */ + correlationId: string; + }; + /** @description DEPRECATED, but some clients need to find the properties */ + LunaStatistics: { + config: external["schemas/playback-channel.json"]["components"]["schemas"]["LunaConfig"]; + data: external["schemas/playback-channel.json"]["components"]["schemas"]["LunaPayload"]; + }; + LunaConfig: { + /** @description Dummy-link to Akamai config. */ + beacon: string; + }; + LunaPayload: { + /** @description Should be blank string */ + title: string; + /** @description Should be blank string */ + device: string; + /** @description Should be blank string */ + playerId: string; + /** @description Should be blank string */ + deliveryType: string; + /** @description Should be blank string */ + playerInfo: string; + /** + * @description Name of selected CDN + * @example Telenor-Cdn + */ + cdnName: string; + }; + /** @enum {string} */ + SnowPlowSource: "prf"; + SnowPlow: { + source: external["schemas/playback-channel.json"]["components"]["schemas"]["SnowPlowSource"]; + }; + /** @enum {string} */ + PlayableStreamingMode: "live"; + /** @description LiveAvailability for channels. */ + LiveAvailability: { + /** @enum {string} */ + type: "channel"; + /** @description Always true for most channels, but not all. Exceptions include NRK3 and NRK Super, which are only on-going for part of the day. */ + isOngoing: boolean; + /** @description Always null since a channel has no transmission interval. */ + transmissionInterval: null; + } | null; + /** + * @example { + * "information": "", + * "isGeoBlocked": false, + * "onDemand": null, + * "live": { + * "type": "channel", + * "isOngoing": true, + * "transmissionInterval": null + * }, + * "externalEmbeddingAllowed": true + * } + */ + Availability: { + /** + * @description Not in use, always an empty string. Intended to be a standardized text about the availability of the media element. + * @enum {string} + */ + information: ""; + /** @description Indicates that the media element may not be played outside the range of Norwegian IP addresses. */ + isGeoBlocked: boolean; + /** @description This is always null since channels aren't on-demand. */ + onDemand: null; + live: external["schemas/playback-channel.json"]["components"]["schemas"]["LiveAvailability"]; + externalEmbeddingAllowed: boolean; + }; + /** + * @description Applies to video channels + * @enum {string} + */ + DisplayAspectRatioVideo: "16:9"; + /** @description Applies to audio channels */ + DisplayAspectRatioAudio: null; + /** @enum {string} */ + PlayableSourceMedium: "audio" | "video"; + /** @description Information that only applies to a non-playable channel. */ + NonPlayableElement: { + reason: external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayableReason"]; + messageType: external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayableMessageType"]; + /** @description Standard message for end user describing why the media element is not playable. */ + endUserMessage: string; + /** @description Message with supplemental info about non-playable status. Should be null whenever userAction is not null. */ + endUserMessageSupplement: string | null; + /** + * @deprecated + * @description Obsolete, help links should be located in the _links property. URL to the help page referred to in the end user message. + */ + helpUrl?: string; + userAction?: external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayableUserAction"]; + /** + * @deprecated + * @description Obsolete, but still expected by some clients. Always null. + */ + availableAbroadUrl?: null; + }; + /** + * @description Indicates an action the user may perform to gain access to blocked content. Currently this only applies to content that has been blocked with message ChannelAvailableWithNorwegianResidentClaim. + * @example { + * "action": "claimResidency", + * "label": "Bekreft med BankID" + * } + */ + NonPlayableUserAction: { + action: external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayableUserActionType"]; + /** @description Text label that the client should use when offering the user an interaction to perform the action. */ + label: string; + } | null; + /** @enum {string} */ + NonPlayableUserActionType: "claimResidency" | "loginAndClaimResidency"; + /** @enum {string} */ + NonPlayableReason: "blocked" | "notransmission" | "unavailable"; + /** @enum {string} */ + NonPlayableMessageType: + | "NoMessage" + | "ChannelIsGeoblocked" + | "ChannelUnavailable" + | "ChannelAvailableWithNorwegianResidentClaim" + | "ChannelExceedsProfileRestrictions"; + /** @enum {string} */ + AvailabilityLabel: "Av"; + /** @enum {string} */ + AvailabilityManifestSubtitleLabel: "Norsk" | "Norsk – på all tale"; + /** @enum {string} */ + ManifestLinkName: "default"; + /** @enum {string} */ + ManifestSubtitleLinkName: "default" | "all-speech-subtitles"; + /** @description A collection of links that the client may present to the user when access to the media has been blocked. */ + NonPlayablePosterLinkCollection: { + href: string; + title: string; + }[]; + /** + * @deprecated + * @description Always null for channels. + */ + SkipDialogInfo: null; + /** + * @deprecated + * @description Always null for channels. + */ + Interaction: null; + /** @description Always null for channels. */ + InteractionPoints: null; + IndexPoint: { + /** @description IndexPoint title */ + title: string; + /** @description IndexPoint Duration */ + startPoint: string; + }; + PosterImage: { + url: string; + pixelWidth: number; + }; + /** @description Image to show before play */ + Poster: { + /** @description List of images in different sizes */ + images: external["schemas/playback-channel.json"]["components"]["schemas"]["PosterImage"][]; + }; + /** @description Title to show before play */ + Titles: { + title: string; + subtitle: string; + }; + Preplay: { + titles: external["schemas/playback-channel.json"]["components"]["schemas"]["Titles"]; + /** @description Description to show before play */ + description: string; + poster: external["schemas/playback-channel.json"]["components"]["schemas"]["Poster"]; + /** @description This list is always empty, since channels don't have index points. */ + indexPoints: external["schemas/playback-channel.json"]["components"]["schemas"]["IndexPoint"][]; + }; + /** @description Channels are always exempt from legal age rating. */ + LegalAgeBodyExempt: { + /** @enum {string} */ + status: "exempt"; + }; + LegalAge: { + /** + * @description The legal basis for the legal age rating. + * Currently, this is 'LOV2015-02-06-7'. + */ + legalReference: string; + body: external["schemas/playback-channel.json"]["components"]["schemas"]["LegalAgeBodyExempt"]; + }; + ResolvePlayable: { + /** + * @description Uri to resolve to get the media manifest. + * Not sure why this is included when the same link is available in the _links collection. + */ + resolve: string; + }; + PlayablePlaybackMetadataResponse: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-channel.json"]["components"]["schemas"]["ResolvePlayable"]; + /** @description This is always null when the content is playable. */ + nonPlayable?: null; + id: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelId"]; + streamingMode: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableStreamingMode"]; + /** @description Always null since channels neither begin nor end. */ + duration: null; + availability: external["schemas/playback-channel.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-channel.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-channel.json"]["components"]["schemas"]["Preplay"]; + displayAspectRatio: + | external["schemas/playback-channel.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-channel.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + skipDialogInfo: external["schemas/playback-channel.json"]["components"]["schemas"]["SkipDialogInfo"]; + interaction: external["schemas/playback-channel.json"]["components"]["schemas"]["Interaction"]; + interactionPoints: external["schemas/playback-channel.json"]["components"]["schemas"]["InteractionPoints"]; + sourceMedium: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + /** @description List of manifestlinks, default */ + manifests: { + playability: "playable"; + /** @description Link to playback/manifest for the channel. */ + href: string; + name: external["schemas/playback-channel.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + /** @description List of all manifestlinks, also to manifests with subtitles */ + manifestSubtitles?: { + playability: "playable"; + /** @description Link to playback/manifest for the channel. */ + href: string; + name: external["schemas/playback-channel.json"]["components"]["schemas"]["ManifestSubtitleLinkName"]; + }[]; + /** @description Always null, since there is no next link for a channel. */ + next: null; + /** @description Always null, since there is no next link for a channel. */ + nextLinks: null; + /** @description Does only apply to TV Channels */ + fargerik?: { + playability: "playable"; + /** @description Absolute URL to fargerik */ + href?: string; + } | null; + /** @description Does only apply to TV Channels with epg */ + epgBuffer?: { + playability: "playable"; + /** @description Relative link to epg live buffer for the channel */ + href: string; + }; + }; + _embedded: { + playability: "playable"; + manifests: + | external["schemas/playback-channel.json"]["components"]["schemas"]["EmbeddedPlaybackManifestResource"][] + | null; + manifestSubtitles?: external["schemas/playback-channel.json"]["components"]["schemas"][ + "EmbeddedPlaybackManifestSubtitlesResource" + ][]; + /** @description Always null, since there is no next link for a channel. */ + next: null; + }; + }; + /** @description Metadata response for non-playable content. */ + NonPlayablePlaybackMetadataResponse: { + /** @enum {string} */ + playability: "nonPlayable"; + /** @description This is always null when the content is non-playable. */ + playable: null; + nonPlayable: external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayableElement"]; + id: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelId"]; + streamingMode: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableStreamingMode"]; + /** @description Always null since channels neither begin nor end. */ + duration: null; + availability: external["schemas/playback-channel.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-channel.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-channel.json"]["components"]["schemas"]["Preplay"]; + displayAspectRatio: + | external["schemas/playback-channel.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-channel.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + interaction: external["schemas/playback-channel.json"]["components"]["schemas"]["Interaction"]; + interactionPoints: external["schemas/playback-channel.json"]["components"]["schemas"]["InteractionPoints"]; + skipDialogInfo: external["schemas/playback-channel.json"]["components"]["schemas"]["SkipDialogInfo"]; + sourceMedium: external["schemas/playback-channel.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + }; + manifests: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-channel.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + /** @description Always null, since there is no next link for a channel. */ + next: null; + /** @description Always null, since there is no next link for a channel. */ + nextLinks: null; + nonPlayablePosterLinks?: + external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayablePosterLinkCollection"]; + }; + _embedded: { + playability: "nonPlayable"; + manifests: { + playability: "nonPlayable"; + id: string; + availabilityLabel: + external["schemas/playback-channel.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-channel.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }[] | null; + }; + }; + PlaybackMetadataResponse: + | external["schemas/playback-channel.json"]["components"]["schemas"]["PlayablePlaybackMetadataResponse"] + | external["schemas/playback-channel.json"]["components"]["schemas"]["NonPlayablePlaybackMetadataResponse"]; + ChannelPlugOverviewItemTv: + | external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelPlug"] + | external["schemas/playback-channel.json"]["components"]["schemas"]["DistrictChannelPromoPlug"]; + ChannelPlug: { + _links: { + /** @description Links to playback/metadata for the channel. */ + self: { + /** @example /playback/metadata/channel/nrk1 */ + href: string; + }; + /** @description Links to playback/manifest for the channel. */ + manifest: { + /** @example /playback/manifest/channel/nrk1 */ + href: string; + }; + /** @description Links to EPG for the channel. */ + epg: { + /** @example /epg/nrk1 */ + href: string; + } | null; + /** @description Links to fargerik for the channel. */ + fargerik: { + /** @example https://fargerik-psapi.nrk.no/tv/channel/nrk1 */ + href: string; + }; + }; + /** @enum {string} */ + plugType: "channel"; + /** + * @description If type is 'districtChannel', there will be a 'districtChannel' property. District channels have a 'parent channel'. District channels will have local content, and broadcast the same as its parent when no local broadcast is scheduled. + * @enum {string} + */ + type: "regionalChannel" | "districtChannel" | "streamingChannel"; + id: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelId"]; + /** @description Channel title */ + title: string; + /** @description Channel description */ + description: string; + /** @description Is channel geoblocked or not */ + isGeoBlocked: boolean; + /** @description Channel image */ + image: { + /** @enum {string} */ + ratio: "16:9"; + /** @enum {string} */ + mimeType: "image/jpeg" | "image/png"; + items: external["schemas/playback-channel.json"]["components"]["schemas"]["PosterImage"][]; + }; + }; + DistrictChannelPromoPlug: { + /** @enum {string} */ + plugType?: "plug"; + /** @description Placeholder for plug */ + placeholder?: { + /** @enum {string} */ + type?: "district-channel-promo"; + }; + }; + ChannelOverviewItemTv: { + _links: { + /** @description Links to playback/metadata for the channel. */ + self: { + /** @example /playback/metadata/channel/nrk1 */ + href: string; + }; + /** @description Links to playback/manifest for the channel. */ + manifest: { + /** @example /playback/manifest/channel/nrk1 */ + href: string; + }; + /** @description Links to EPG for the channel. */ + epg: { + /** @example /epg/nrk1 */ + href: string; + } | null; + /** @description Links to fargerik for the channel. */ + fargerik: { + /** @example https://fargerik-psapi.nrk.no/tv/channel/nrk1 */ + href: string; + }; + }; + id: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelId"]; + /** + * @description If type is 'districtChannel', there will be a 'districtChannel' property. District channels have a 'parent channel'. District channels will have local content, and broadcast the same as its parent when no local broadcast is scheduled. A streaming channel will have content just available online. + * @enum {string} + */ + type: "regionalChannel" | "districtChannel" | "streamingChannel"; + /** @description Embedded representation of playback metadata. The 'playback' link relation is unfortunate, 'metadata' would be more appropriate. */ + _embedded: { + playback: + external["schemas/playback-channel.json"]["components"]["schemas"]["EmbeddedPlaybackMetadataResource"]; + }; + }; + ChannelOverviewItemRadio: { + _links: { + /** @description Links to playback/metadata for the channel. */ + self: { + /** @example /playback/metadata/channel/nrk1 */ + href: string; + }; + /** @description Links to playback/manifest for the channel. */ + manifest: { + /** @example /playback/manifest/channel/nrk1 */ + href: string; + }; + /** @description Links to EPG for the channel. */ + epg: { + /** @example /epg/nrk1 */ + href: string; + } | null; + }; + id: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelId"]; + /** + * @description If type is 'districtChannel', there will be a 'districtChannel' property. District channels have a 'parent channel'. District channels will have local content, and broadcast the same as its parent when no local broadcast is scheduled. + * @enum {string} + */ + type: "regionalChannel" | "districtChannel"; + /** @description Embedded representation of playback metadata. The 'playback' link relation is unfortunate, 'metadata' would be more appropriate. */ + _embedded: { + playback: + external["schemas/playback-channel.json"]["components"]["schemas"]["EmbeddedPlaybackMetadataResource"]; + }; + }; + /** @description Array of channel plugs. NRK Super if contentGroup=children. District channel if valid regionId set */ + ChannelPlugsOverviewResponseTv: + external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelPlugOverviewItemTv"][]; + ChannelOverviewResponseTv: + external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelOverviewItemTv"][]; + ChannelOverviewResponseRadio: + external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelOverviewItemRadio"][]; + ChannelPosterImage: { + image: { + defaultImage: boolean; + /** @enum {string} */ + ratio: "16:9" | "1:1" | "2:3"; + /** @enum {string} */ + mimeType: "image/jpeg" | "image/png"; + items: external["schemas/playback-channel.json"]["components"]["schemas"]["PosterImage"][]; + }; + }; + EmbeddedPlaybackManifestSubtitlesResource: { + id: string; + availabilityLabel: + external["schemas/playback-channel.json"]["components"]["schemas"]["AvailabilityManifestSubtitleLabel"]; + _links: { + self: { + href: string; + name: external["schemas/playback-channel.json"]["components"]["schemas"]["ManifestSubtitleLinkName"]; + }; + }; + }; + EmbeddedPlaybackManifestResource: { + id: string; + availabilityLabel: external["schemas/playback-channel.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + self: { + href: string; + name: external["schemas/playback-channel.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }; + EmbeddedPlaybackMetadataResource: { + title: string; + description: string; + isGeoBlocked: boolean; + posters: external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelPosterImage"][]; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; + }; + $defs: Record; + }; + "schemas/playback-clip.json": { + paths: Record; + webhooks: Record; + components: { + schemas: { + /** + * Format: uuid + * @example 1eb978e6-1535-4101-a0c5-19f083564682 + */ + PotionClipId: string; + /** + * Format: guriClipId + * @example 1234567890 + */ + GuriClipId: string; + /** + * Format: fossClipId + * @example f5555 + */ + FossClipId: string; + ClipId: + | external["schemas/playback-clip.json"]["components"]["schemas"]["PotionClipId"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["GuriClipId"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["FossClipId"]; + HrefLink: { + href: string; + }; + PlaybackManifestResponse: + | external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableManifest"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayableManifest"]; + PlayableManifest: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableElement"]; + statistics: { + playability: "playable"; + scores: external["schemas/playback-clip.json"]["components"]["schemas"]["ScoresStatistics"]; + ga: external["schemas/playback-clip.json"]["components"]["schemas"]["GoogleAnalytics"]; + conviva: external["schemas/playback-clip.json"]["components"]["schemas"]["ConvivaStatistics"]; + luna: external["schemas/playback-clip.json"]["components"]["schemas"]["LunaStatistics"]; + qualityOfExperience: external["schemas/playback-clip.json"]["components"]["schemas"]["QoEStatistics"]; + snowplow: external["schemas/playback-clip.json"]["components"]["schemas"]["SnowPlow"]; + }; + nonPlayable: null; + id: string; + streamingMode: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["schemas/playback-clip.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + sourceMedium: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + metadata: { + playability: "playable"; + href?: string; + /** @enum {string} */ + name?: "metadata"; + }; + }; + }; + NonPlayableManifest: { + /** @enum {string} */ + playability: "nonPlayable"; + /** @description This is always null when the content is non-playable. */ + playable: null; + statistics: { + playability: "nonPlayable"; + /** @description Always null for nonPlayable */ + scores: null; + /** @description Always null for nonPlayable */ + ga: null; + /** @description Always null for nonPlayable */ + luna: null; + /** @description Always null for nonPlayable */ + qualityOfExperience: null; + /** @description Always null for nonPlayable */ + snowplow: null; + }; + nonPlayable: external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayableElement"]; + id: external["schemas/playback-clip.json"]["components"]["schemas"]["ClipId"]; + streamingMode: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["schemas/playback-clip.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + sourceMedium: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + }; + metadata: { + playability: "nonPlayable"; + href: string; + /** @enum {string} */ + name: "metadata"; + }; + nonPlayablePosterLinks?: + external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayablePosterLinkCollection"]; + }; + }; + /** @description Information that only applies to playable content. */ + PlayableElement: { + endSequenceStartTime: string | null; + /** + * Format: ISO8601-duration + * @description For on-demand clips, duration as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + /** @description List of mediaAssets. Currently only one item will be provided, the HLS format, but in the future this list might be expanded to contain different types of assets. */ + assets: external["schemas/playback-clip.json"]["components"]["schemas"]["MediaUrl"][]; + liveBuffer: external["schemas/playback-clip.json"]["components"]["schemas"]["LiveBuffer"]; + /** @description An empty list, since podcasts don't have thumbnails. */ + thumbnails: unknown[]; + /** @description List of subtitles for clients that do side-loading of subtitles. Subtitles are in the [.vtt format](https://en.wikipedia.org/wiki/WebVTT). This is also true for subtitles linked in the manifest files. */ + subtitles: external["schemas/playback-clip.json"]["components"]["schemas"]["Subtitles"][]; + }; + /** @description Information that only applies to playable content. */ + RadioClipPlayableElement: { + endSequenceStartTime: string | null; + /** + * Format: ISO8601-duration + * @description For on-demand clips, duration as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + /** @description List of mediaAssets. Currently only one item will be provided, the HLS format, but in the future this list might be expanded to contain different types of assets. */ + assets: external["schemas/playback-clip.json"]["components"]["schemas"]["MediaUrl"][]; + liveBuffer: external["schemas/playback-clip.json"]["components"]["schemas"]["LiveBuffer"]; + /** @description List of subtitles for clients that do side-loading of subtitles. Subtitles are in the [.vtt format](https://en.wikipedia.org/wiki/WebVTT). This is also true for subtitles linked in the manifest files. */ + subtitles: external["schemas/playback-clip.json"]["components"]["schemas"]["Subtitles"][]; + }; + MediaUrl: { + /** @description The media URL itself. */ + url: string; + format: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableFormat"]; + mimeType: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableMimeType"]; + /** @description True if program is marked as HDSecure, false otherwise */ + encrypted: boolean; + }; + /** + * @description The video format for the resource identified by the URL. + * @enum {string} + */ + PlayableFormat: "HLS" | "MP4" | "MP3"; + /** + * @description The mimetype that corresponds to the specified format. + * @enum {string} + */ + PlayableMimeType: "application/mp4" | "application/vnd.apple.mpegurl" | "audio/mp4" | "video/mp4" | "audio/mp3"; + /** @description Information about the live buffer for live clips. Null for on-demand clips. */ + LiveBuffer: { + /** @description This is always null. The idea was to provide the start point for growing live buffers but the API doesn't have this information. It must be read from the manifest itself. */ + bufferStartTime: null; + /** + * Format: ISO8601-duration + * @description The length of the live buffer as an ISO 8601 formatted string. However, buffer duration doesn't really make sense for growing buffers, hence the duration is set to zero (PT0S). + * @example PT0S + */ + bufferDuration: string; + /** + * @description The type of live buffer. + * @enum {string} + */ + bufferType: "growing"; + } | null; + Subtitles: { + type: external["schemas/playback-clip.json"]["components"]["schemas"]["SubtitleType"]; + language: external["schemas/playback-clip.json"]["components"]["schemas"]["SubtitleLanguage"]; + label: external["schemas/playback-clip.json"]["components"]["schemas"]["SubtitleLabel"]; + defaultOn: boolean; + webVtt: string; + }; + /** @enum {string} */ + SubtitleType: "gbr" | "mix" | "nor" | "ttv"; + /** @enum {string} */ + SubtitleLanguage: "en" | "nb"; + /** @enum {string} */ + SubtitleLabel: "English" | "Norsk" | "Tekstet for hørselshemmede" | "Norsk – på all tale"; + /** @description This payload should be submitted as-is to Scores. */ + ScoresStatistics: + | ({ + /** + * @description Possible values are 'nrkstream' and 'nrkradio' for production, 'test' for other environments. + * @example nrkstream + */ + springStreamSite: string; + /** + * @description A structured string describing the content. + * Example: 'klipp/odm/nrk_online/nyheter/bading-paa-bryggen-i-bergen.39b725c9-d2ed-470c-a2f1-58924a81184'. + * @example klipp/odm/nrk_online/nyheter/bading-paa-bryggen-i-bergen.39b725c9-d2ed-470c-a2f1-58924a81184 + */ + springStreamStream: string; + /** + * @description A structured string describing the client. + * Examples: 'desktop', 'mobil/app/android', 'tv/smart_tv/samsung' etc. + * @example tv/smart_tv/samsung + */ + springStreamContentType: string; + springStreamProgramId: external["schemas/playback-clip.json"]["components"]["schemas"]["ClipId"]; + /** + * @description Duration of the clip in seconds. + * @example 143 + */ + springStreamDuration: number | null; + }) + | null; + /** + * @description * dimension1: ID. KILDESYSTEM:UNIK_IDENTIFIKATOR + * * dimension2: Tittel + * * dimension3: Publiseringsår. + * * dimension4: Publiseringsmåned. + * * dimension5: Publiseringsdag. [01, 31] + * * dimension10: Kilde-ID + * * dimension21: Serie. Kun relevant for episoder/instalments. + * * dimension22: Episodenummer. Kun relevant for episoder (instalments?). + * * dimension23: Kategori + * * dimension25: Mediatype [video | audio] + * * dimension26: Mediakategori [live | ondemand] + * * dimension29: Platform + * + * [Ekstern doc](https://github.com/nrkno/analytics-docs/blob/master/docs/schema.md) + */ + GoogleAnalytics: { + [key: string]: string; + }; + /** @description DEPRECATED! Should be null! */ + ConvivaStatistics: { + /** @enum {string} */ + playerName?: ""; + /** @enum {string} */ + assetName?: ""; + /** @enum {number} */ + duration?: -1; + /** @enum {string} */ + streamType?: ""; + /** @enum {string} */ + streamUrl?: ""; + custom?: external["schemas/playback-clip.json"]["components"]["schemas"]["ConvivaCustomProperties"]; + } | null; + /** @description Only allow empty string since Conviva is not in use. */ + ConvivaCustomProperties: { + /** @enum {string} */ + contentId: ""; + /** @enum {string} */ + series: ""; + /** @enum {string} */ + title: ""; + /** @enum {string} */ + episode: ""; + /** @enum {string} */ + category: ""; + /** @enum {string} */ + mediaType: ""; + /** @enum {string} */ + cdnName: ""; + /** @enum {string} */ + applicationVersion: ""; + /** @enum {string} */ + applicationName: ""; + /** @enum {string} */ + serviceName: ""; + /** @enum {string} */ + contentType: ""; + }; + QoEStatistics: { + /** @description DeviceGroup and Name detected by API */ + clientName: string; + /** @description CdnName for playable URL */ + cdnName: string; + /** @description StreamingFormat string representation */ + streamingFormat: string; + /** @description SegmentLength string representation */ + segmentLength: string; + /** + * @description Asset type, calculated based on API response + * @enum {string} + */ + assetType: "ondemand" | "event" | "live"; + /** @description Request correlation Id */ + correlationId: string; + }; + /** @description DEPRECATED, but some clients need to find the properties */ + LunaStatistics: { + config: external["schemas/playback-clip.json"]["components"]["schemas"]["LunaConfig"]; + data: external["schemas/playback-clip.json"]["components"]["schemas"]["LunaPayload"]; + }; + LunaConfig: { + /** @description Dummy-link to Akamai config. */ + beacon: string; + }; + LunaPayload: { + /** @description Should be blank string */ + title: string; + /** @description Should be blank string */ + device: string; + /** @description Should be blank string */ + playerId: string; + /** @description Should be blank string */ + deliveryType: string; + /** @description Should be blank string */ + playerInfo: string; + /** + * @description Name of selected CDN + * @example Telenor-Cdn + */ + cdnName: string; + }; + /** @enum {string} */ + SnowPlowSource: "potion"; + SnowPlow: { + source: external["schemas/playback-clip.json"]["components"]["schemas"]["SnowPlowSource"]; + }; + /** @enum {string} */ + PlayableStreamingMode: "live" | "onDemand"; + /** @description Only applicable for live media elements (event clips). Will be null for on-demand media elements. */ + LiveAvailability: { + /** @enum {string} */ + type: "transmission"; + /** @description Indicates that the transmission is currently on-going (the time is within the boundaries of the transmission interval). */ + isOngoing: boolean; + transmissionInterval: + external["schemas/playback-clip.json"]["components"]["schemas"]["LiveTransmissionInterval"]; + } | null; + /** + * @example { + * "information": "", + * "isGeoBlocked": true, + * "onDemand": { + * "from": "2015-10-02T20:30:00+02:00", + * "to": "9999-12-31T00:00:00+01:00", + * "hasRightsNow": true + * }, + * "live": null, + * "externalEmbeddingAllowed": true + * } + */ + Availability: { + /** @description Not in use, always an empty string. Intended to be a standardized text about the availability of the media element. */ + information: string; + /** @description Indicates that the media element may not be played outside the range of Norwegian IP addresses. */ + isGeoBlocked: boolean; + onDemand: external["schemas/playback-clip.json"]["components"]["schemas"]["OnDemandUsageRights"]; + live: external["schemas/playback-clip.json"]["components"]["schemas"]["LiveAvailability"]; + externalEmbeddingAllowed: boolean; + }; + /** @description Start and end time for a live transmission. This applies to FOSS clips for 'live events'. It can be degenerate (both from and to are null) in case there are no actual usage rights for the live transmission. */ + LiveTransmissionInterval: { + /** Format: date-time */ + from: string | null; + /** Format: date-time */ + to: string | null; + }; + /** @description Only applicable for on-demand media elements. Will be null for live media elements. It can be degenerate (both from and to are null) in case no usage rights exist for the on-demand content. Obviously hasRightsNow will be false in that case. */ + OnDemandUsageRights: + | ({ + /** Format: date-time */ + from?: string | null; + /** Format: date-time */ + to?: string | null; + /** @description Indicates whether or not the usage rights are valid at this point in time. This is true if there are usage rights available, and the current time is within the time interval for the usage rights. */ + hasRightsNow: boolean; + }) + | null; + /** + * @description Applies to video clips + * @enum {string} + */ + DisplayAspectRatioVideo: "16:9" | "1:1" | "2:3" | "4:3" | "9:16"; + /** @description Applies to audio clips */ + DisplayAspectRatioAudio: null; + /** @enum {string} */ + PlayableSourceMedium: "audio" | "video"; + /** @description Information that only applies to non-playable clips. */ + NonPlayableElement: { + reason: external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayableReason"]; + messageType: external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayableMessageType"]; + /** @description Standard message for end user describing why the media element is not playable. */ + endUserMessage: string; + /** @description Message with supplemental info about non-playable status. Should be null whenever userAction is not null. */ + endUserMessageSupplement: string | null; + /** + * @deprecated + * @description Obsolete, help links should be located in the _links property. URL to the help page referred to in the end user message. + */ + helpUrl?: string; + userAction?: external["schemas/playback-clip.json"]["components"]["schemas"]["NullType"]; + /** + * @deprecated + * @description Obsolete, but still expected by some clients. Always null. + */ + availableAbroadUrl?: null; + }; + /** @enum {string} */ + NonPlayableReason: "blocked" | "notransmission" | "unavailable"; + /** @enum {string} */ + NonPlayableMessageType: + | "NoMessage" + | "ProgramIsGeoblocked" + | "ProgramIsInLiveBuffer" + | "ProgramIsNotTranscoded" + | "ProgramRightsHasExpired" + | "ProgramRightsHasNotStarted" + | "ProgramUnavailable"; + /** @enum {string} */ + AvailabilityLabel: "Av"; + /** @enum {string} */ + ManifestLinkName: "default"; + /** @description A collection of links that the client may present to the user when access to the media has been blocked. */ + NonPlayablePosterLinkCollection: { + href: string; + title: string; + }[]; + /** @description Always null for clips. */ + NullType: null; + IndexPoint: { + /** @description IndexPoint title */ + title: string; + /** @description IndexPoint Duration */ + startPoint: string; + }; + PosterImage: { + url: string; + pixelWidth: number; + }; + /** @description Image to show before play */ + Poster: { + /** @description List of images in different sizes */ + images: external["schemas/playback-clip.json"]["components"]["schemas"]["PosterImage"][]; + }; + /** @description Title to show before play */ + Titles: { + title: string; + subtitle: string; + }; + Preplay: { + titles: external["schemas/playback-clip.json"]["components"]["schemas"]["Titles"]; + /** @description Description to show before play. */ + description: string; + poster: external["schemas/playback-clip.json"]["components"]["schemas"]["Poster"]; + /** @description List of index points. For clips, the list is empty. */ + indexPoints: external["schemas/playback-clip.json"]["components"]["schemas"]["IndexPoint"][]; + }; + /** @description Clips are always exempt from legal age rating. */ + LegalAgeBodyExempt: { + /** @enum {string} */ + status: "exempt"; + }; + LegalAge: { + /** + * @description The legal basis for the legal age rating. + * Currently, this is 'LOV2015-02-06-7'. + */ + legalReference: string; + body: external["schemas/playback-clip.json"]["components"]["schemas"]["LegalAgeBodyExempt"]; + }; + ResolvePlayable: { + /** + * @description Uri to resolve to get the media manifest. + * Not sure why this is included when the same link is available in the _links collection. + */ + resolve: string; + }; + PlayablePlaybackMetadataResponse: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-clip.json"]["components"]["schemas"]["ResolvePlayable"]; + /** @description This is always null when the clip is playable. */ + nonPlayable: null; + id: external["schemas/playback-clip.json"]["components"]["schemas"]["ClipId"]; + streamingMode: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableStreamingMode"]; + /** + * Format: ISO8601-duration + * @description Duration of the clip as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + availability: external["schemas/playback-clip.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-clip.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-clip.json"]["components"]["schemas"]["Preplay"]; + displayAspectRatio: + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + skipDialogInfo: external["schemas/playback-clip.json"]["components"]["schemas"]["NullType"]; + interaction: external["schemas/playback-clip.json"]["components"]["schemas"]["NullType"]; + interactionPoints: external["schemas/playback-clip.json"]["components"]["schemas"]["NullType"]; + sourceMedium: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + manifests: { + playability: "playable"; + href: string; + name: external["schemas/playback-clip.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + /** @description Always null, since there is no next link for a clip. */ + next: null; + /** @description Always null, since there is no next link for a clip. */ + nextLinks: null; + /** @description When present progress should be reported */ + progress?: { + playability: "playable"; + href: string; + /** @enum {boolean} */ + templated: true; + }; + }; + _embedded: { + playability: "playable"; + manifests: + | external["schemas/playback-clip.json"]["components"]["schemas"]["EmbeddedPlaybackManifestResource"][] + | null; + /** @description Always null, since there is no next link for a clip. */ + next: null; + }; + }; + /** @description Metadata response for non-playable clips. */ + NonPlayablePlaybackMetadataResponse: { + /** @enum {string} */ + playability: "nonPlayable"; + /** @description This is always null when the clip is non-playable. */ + playable: null; + nonPlayable: external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayableElement"]; + id: external["schemas/playback-clip.json"]["components"]["schemas"]["ClipId"]; + streamingMode: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableStreamingMode"]; + /** + * Format: ISO8601-duration + * @description Duration of the clip as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + availability: external["schemas/playback-clip.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-clip.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-clip.json"]["components"]["schemas"]["Preplay"]; + displayAspectRatio: + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + interaction: external["schemas/playback-clip.json"]["components"]["schemas"]["NullType"]; + interactionPoints: external["schemas/playback-clip.json"]["components"]["schemas"]["NullType"]; + skipDialogInfo: external["schemas/playback-clip.json"]["components"]["schemas"]["NullType"]; + sourceMedium: external["schemas/playback-clip.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + }; + manifests: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-clip.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + /** @description Always null, since there is no next link for a podcast. */ + next: null; + /** @description Always null, since there is no next link for a podcast. */ + nextLinks: null; + /** @description When present, clients should report progress using this link. */ + progress?: { + playability: "nonPlayable"; + /** @example /tv/userdata/{userId}/progress/programs/MUHH32001019 */ + href: string; + /** @enum {boolean} */ + templated: true; + /** @enum {string} */ + name?: "progress"; + }; + nonPlayablePosterLinks?: + external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayablePosterLinkCollection"]; + }; + _embedded: { + playability: "nonPlayable"; + manifests: { + playability: "nonPlayable"; + id: string; + availabilityLabel: external["schemas/playback-clip.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-clip.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }[] | null; + }; + }; + PlaybackMetadataResponse: + | external["schemas/playback-clip.json"]["components"]["schemas"]["PlayablePlaybackMetadataResponse"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayablePlaybackMetadataResponse"]; + EmbeddedPlaybackManifestResource: { + id: string; + availabilityLabel: external["schemas/playback-clip.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + self: { + href: string; + name: external["schemas/playback-clip.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }; + /** + * Format: uuid + * @example 442667f6-d6cf-3bdb-7603-cf938eb97944 + */ + RadioClipId: string; + /** @enum {string} */ + RadioClipType: "news"; + /** @description Playback metadata response for radio clips */ + RadioClipPlaybackMetadataResponse: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-clip.json"]["components"]["schemas"]["ResolvePlayable"]; + /** @description This is always null when the clip is playable. */ + nonPlayable: null; + id: external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipId"]; + streamingMode: + external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPlayableStreamingMode"]; + /** + * Format: ISO8601-duration + * @description Duration of the program as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + availability: external["schemas/playback-clip.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-clip.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPreplay"]; + sourceMedium: + external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPlayableSourceMedium"]; + _links: { + self: { + href: string; + }; + manifests: { + href: string; + name: external["schemas/playback-clip.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + /** @description When present progress should be reported */ + progress?: { + href: string; + /** @enum {boolean} */ + templated: true; + }; + }; + _embedded: { + manifests: + | external["schemas/playback-clip.json"]["components"]["schemas"]["EmbeddedPlaybackManifestResource"][] + | null; + }; + }; + RadioClipPlaybackManifestResponse: + | external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPlayableManifest"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["NonPlayableManifest"]; + RadioClipPlayableManifest: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPlayableElement"]; + statistics: { + playability: "playable"; + scores?: external["schemas/playback-clip.json"]["components"]["schemas"]["ScoresStatistics"]; + ga: external["schemas/playback-clip.json"]["components"]["schemas"]["GoogleAnalytics"]; + conviva?: external["schemas/playback-clip.json"]["components"]["schemas"]["ConvivaStatistics"]; + luna?: external["schemas/playback-clip.json"]["components"]["schemas"]["LunaStatistics"]; + snowplow: external["schemas/playback-clip.json"]["components"]["schemas"]["SnowPlow"]; + }; + nonPlayable: null; + id: string; + streamingMode: + external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPlayableStreamingMode"]; + availability: external["schemas/playback-clip.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-clip.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + sourceMedium: + external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPlayableSourceMedium"]; + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + metadata: { + playability: "playable"; + href?: string; + /** @enum {string} */ + name?: "metadata"; + }; + }; + }; + /** + * @description Radio clips are always on-demand. + * @enum {string} + */ + RadioClipPlayableStreamingMode: "onDemand"; + /** + * @description Radio clips are always audio. + * @enum {string} + */ + RadioClipPlayableSourceMedium: "audio"; + /** @description Image to show when playing radio clip */ + RadioClipPoster: { + credit?: string; + /** @description List of images in different sizes */ + images: external["schemas/playback-clip.json"]["components"]["schemas"]["PosterImage"][]; + }; + RadioClipPreplay: { + titles: external["schemas/playback-clip.json"]["components"]["schemas"]["Titles"]; + /** @description Description to show before play */ + description: string; + poster: external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPoster"]; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; + }; + $defs: Record; + }; + "schemas/playback-podcast.json": { + paths: Record; + webhooks: Record; + components: { + schemas: { + /** + * Format: hawaiiPodcastId + * @example nrkno-poddkast-10908-146680-20122018123000 + */ + HawaiiPodcastId: string; + /** + * Format: bbcPodcastId + * @example urnbbcpodcastp069nvqv + */ + BbcPodcastId: string; + /** + * Format: radioarkivPodcastEpisodeId + * @example l_27697626-cf0d-4bca-a976-26cf0d5bca21 + */ + RadioarkivPodcastEpisodeId: string; + PodcastEpisodeId: + | external["schemas/playback-podcast.json"]["components"]["schemas"]["HawaiiPodcastId"] + | external["schemas/playback-podcast.json"]["components"]["schemas"]["BbcPodcastId"] + | external["schemas/playback-podcast.json"]["components"]["schemas"]["RadioarkivPodcastEpisodeId"]; + /** @example radioresepsjonen */ + PodcastSeriesId: string; + HrefLink: { + href: string; + }; + PlaybackManifestResponse: + | external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableManifest"] + | external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayableManifest"]; + PlayableManifest: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableElement"]; + statistics: { + playability: "playable"; + scores: external["schemas/playback-podcast.json"]["components"]["schemas"]["ScoresStatistics"]; + ga: external["schemas/playback-podcast.json"]["components"]["schemas"]["GoogleAnalytics"]; + luna: external["schemas/playback-podcast.json"]["components"]["schemas"]["LunaStatistics"]; + qualityOfExperience: external["schemas/playback-podcast.json"]["components"]["schemas"]["QoEStatistics"]; + snowplow: external["schemas/playback-podcast.json"]["components"]["schemas"]["SnowPlow"]; + }; + nonPlayable: null; + id: external["schemas/playback-podcast.json"]["components"]["schemas"]["PodcastEpisodeId"]; + streamingMode: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["schemas/playback-podcast.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + sourceMedium: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + metadata: { + playability: "playable"; + href?: string; + /** @enum {string} */ + name?: "metadata"; + }; + }; + }; + NonPlayableManifest: { + /** @enum {string} */ + playability: "nonPlayable"; + /** @description This is always null when the podcast is non-playable. */ + playable: null; + statistics: { + playability: "nonPlayable"; + /** @description Always null for nonPlayable */ + scores: null; + /** @description Always null for nonPlayable */ + ga: null; + /** @description Always null for nonPlayable */ + luna: null; + /** @description Always null for nonPlayable */ + qualityOfExperience: null; + /** @description Always null for nonPlayable */ + snowplow: null; + }; + nonPlayable: external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayableElement"]; + id: external["schemas/playback-podcast.json"]["components"]["schemas"]["PodcastEpisodeId"]; + streamingMode: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["schemas/playback-podcast.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + sourceMedium: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + }; + metadata: { + playability: "nonPlayable"; + href: string; + /** @enum {string} */ + name: "metadata"; + }; + nonPlayablePosterLinks?: + external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayablePosterLinkCollection"]; + }; + }; + /** @description Information that only applies to playable content. */ + PlayableElement: { + endSequenceStartTime: string | null; + /** + * Format: ISO8601-duration + * @description Duration of the podcast episode as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + /** @description List of media assets. Currently only one item will be provided, the HLS format, but in the future this list might be expanded to contain different types of assets. */ + assets: external["schemas/playback-podcast.json"]["components"]["schemas"]["MediaUrl"][]; + liveBuffer: external["schemas/playback-podcast.json"]["components"]["schemas"]["LiveBuffer"]; + /** @description An empty list, since podcasts don't have thumbnails. */ + thumbnails: unknown[]; + /** @description An empty list, since podcasts don't have subtitles. */ + subtitles: unknown[]; + }; + MediaUrl: { + /** @description The media URL itself. */ + url: string; + format: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableFormat"]; + mimeType: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableMimeType"]; + /** @description True if program is marked as HDSecure, false otherwise */ + encrypted: boolean; + }; + /** + * @description The format for the media asset identified by the URL. + * @enum {string} + */ + PlayableFormat: "MP3"; + /** + * @description The mimetype that corresponds to the specified format. + * @enum {string} + */ + PlayableMimeType: "audio/mp3"; + /** @description Always null for podcast episodes. */ + LiveBuffer: null; + /** @description Thumbnails item. The thumbnails follow the displayAspectRatio for the channel, which can be used to derive height from width. Only relevant for video channels, null otherwise. */ + ThumbnailsItem: { + /** @description Url to thumbnails feed in .vtt format. */ + url: string; + /** @description Width of the thumbnail. */ + width: number; + }; + Subtitles: { + type: external["schemas/playback-podcast.json"]["components"]["schemas"]["SubtitleType"]; + language: external["schemas/playback-podcast.json"]["components"]["schemas"]["SubtitleLanguage"]; + label: external["schemas/playback-podcast.json"]["components"]["schemas"]["SubtitleLabel"]; + defaultOn: boolean; + webVtt: string; + }; + /** @enum {string} */ + SnowPlowSource: "podcast"; + SnowPlow: { + source: external["schemas/playback-podcast.json"]["components"]["schemas"]["SnowPlowSource"]; + }; + /** @enum {string} */ + SubtitleType: "gbr" | "mix" | "nor" | "ttv"; + /** @enum {string} */ + SubtitleLanguage: "en" | "nb"; + /** @enum {string} */ + SubtitleLabel: "English" | "Norsk" | "Tekstet for hørselshemmede" | "Norsk – på all tale"; + /** @description Always null for podcast. */ + ScoresStatistics: null; + /** + * @description * dimension1: ID. KILDESYSTEM:UNIK_IDENTIFIKATOR + * * dimension2: Tittel + * * dimension3: Publiseringsår. + * * dimension4: Publiseringsmåned. + * * dimension5: Publiseringsdag. [01, 31] + * * dimension10: Kilde-ID + * * dimension21: Serie. Kun relevant for episoder/instalments. + * * dimension22: Episodenummer. Kun relevant for episoder (instalments?). + * * dimension23: Kategori + * * dimension25: Mediatype [video | audio] + * * dimension26: Mediakategori [live | ondemand] + * * dimension29: Platform + * + * [Ekstern doc](https://github.com/nrkno/analytics-docs/blob/master/docs/schema.md) + */ + GoogleAnalytics: { + [key: string]: string; + }; + QoEStatistics: { + /** @description DeviceGroup and Name detected by API */ + clientName: string; + /** @description CdnName for playable URL */ + cdnName: string; + /** @description StreamingFormat string representation */ + streamingFormat: string; + /** @description SegmentLength string representation */ + segmentLength: string; + /** + * @description Asset type, calculated based on API response + * @enum {string} + */ + assetType: "ondemand"; + /** @description Request correlation Id */ + correlationId: string; + }; + /** @description DEPRECATED, but some clients need to find the properties */ + LunaStatistics: { + config: external["schemas/playback-podcast.json"]["components"]["schemas"]["LunaConfig"]; + data: external["schemas/playback-podcast.json"]["components"]["schemas"]["LunaPayload"]; + }; + LunaConfig: { + /** @description Dummy-link to Akamai config. */ + beacon: string; + }; + LunaPayload: { + /** @description Should be blank string */ + title: string; + /** @description Should be blank string */ + device: string; + /** @description Should be blank string */ + playerId: string; + /** @description Should be blank string */ + deliveryType: string; + /** @description Should be blank string */ + playerInfo: string; + /** + * @description Name of selected CDN + * @example Telenor-Cdn + */ + cdnName: string; + }; + /** + * @description Podcasts are always on-demand. + * @enum {string} + */ + PlayableStreamingMode: "onDemand"; + /** @description Always null, since podcasts are always on-demand. */ + LiveAvailability: null; + /** + * @example { + * "information": "", + * "isGeoBlocked": true, + * "onDemand": { + * "from": "2015-10-02T20:30:00+02:00", + * "to": "9999-12-31T00:00:00+01:00", + * "hasRightsNow": true + * }, + * "live": null, + * "externalEmbeddingAllowed": true + * } + */ + Availability: { + /** @description Not in use, always an empty string. Intended to be a standardized text about the availability of the media element. */ + information: string; + /** @description Indicates that the media element may not be played outside the range of Norwegian IP addresses. */ + isGeoBlocked: boolean; + onDemand: external["schemas/playback-podcast.json"]["components"]["schemas"]["OnDemandUsageRights"]; + live: external["schemas/playback-podcast.json"]["components"]["schemas"]["LiveAvailability"]; + externalEmbeddingAllowed: boolean; + }; + /** @description Always null for podcast episodes. */ + NullType: null; + /** @description It can be degenerate (both from and to are null) in case no usage rights exist. Obviously hasRightsNow will be false in that case. */ + OnDemandUsageRights: { + /** Format: date-time */ + from?: string | null; + /** Format: date-time */ + to?: string | null; + /** @description Indicates whether or not the usage rights are valid at this point in time. This is true if there are usage rights available, and the current time is within the time interval for the usage rights. */ + hasRightsNow: boolean; + }; + /** + * @description Podcast episodes are always audio. + * @enum {string} + */ + PlayableSourceMedium: "audio"; + /** @description Information that only applies to a non-playable podcast. */ + NonPlayableElement: { + reason: external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayableReason"]; + messageType: external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayableMessageType"]; + /** @description Standard message for end user describing why the media element is not playable. */ + endUserMessage: string; + /** @description Message with supplemental info about non-playable status. Should be null whenever userAction is not null. */ + endUserMessageSupplement: string | null; + /** + * @deprecated + * @description Obsolete, help links should be located in the _links property. URL to the help page referred to in the end user message. + */ + helpUrl?: string; + userAction?: external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayableUserAction"]; + /** + * @deprecated + * @description Obsolete, but still expected by some clients. Always null. + */ + availableAbroadUrl?: null; + }; + /** @description Indicates an action the user may perform to gain access to blocked content. */ + NonPlayableUserAction: { + action: external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayableUserActionType"]; + /** @description Text label that the client should use when offering the user an interaction to perform the action. */ + label: string; + } | null; + NonPlayableUserActionType: string; + /** @enum {string} */ + NonPlayableReason: "blocked" | "notransmission" | "unavailable"; + /** @enum {string} */ + NonPlayableMessageType: + | "ChannelIsGeoblocked" + | "ChannelUnavailable" + | "NoMessage" + | "ProgramIsGeoblocked" + | "ProgramIsInLiveBuffer" + | "ProgramIsNotTranscoded" + | "ProgramRightsHasExpired" + | "ProgramRightsHasNotStarted" + | "ProgramUnavailable"; + /** @enum {string} */ + AvailabilityLabel: "Av"; + SeriesLink: { + href: string; + /** @enum {string} */ + name: "series"; + }; + /** @enum {string} */ + ManifestLinkName: "default"; + /** @description A collection of links that the client may present to the user when access to the media has been blocked. */ + NonPlayablePosterLinkCollection: { + href: string; + title: string; + }[]; + IndexPoint: { + /** @description IndexPoint title */ + title: string; + /** @description IndexPoint Duration */ + startPoint: string; + }; + PosterImage: { + url: string; + pixelWidth: number; + }; + /** @description Image to show before play. Fallback to podcast series image if episode has no image. */ + Poster: { + /** @description List of images in different sizes */ + images: external["schemas/playback-podcast.json"]["components"]["schemas"]["PosterImage"][]; + }; + SquarePosterImage: { + url: string; + pixelWidth: number; + }; + /** @description Square image for podcast episode to show before play. Fallback to podcast series square image if episode has no square image. */ + SquarePoster: { + /** @description List of images in different sizes */ + images: external["schemas/playback-podcast.json"]["components"]["schemas"]["SquarePosterImage"][]; + }; + /** @description Title to show before play */ + Titles: { + title: string; + subtitle: string; + }; + Preplay: { + titles: external["schemas/playback-podcast.json"]["components"]["schemas"]["Titles"]; + /** @description Description to show before play */ + description: string; + poster: external["schemas/playback-podcast.json"]["components"]["schemas"]["Poster"]; + squarePoster?: external["schemas/playback-podcast.json"]["components"]["schemas"]["SquarePoster"]; + /** @description List of index points */ + indexPoints: external["schemas/playback-podcast.json"]["components"]["schemas"]["IndexPoint"][]; + }; + LegalAgeRating: { + code: string; + displayValue: string; + displayAge: string; + }; + LegalAgeBodyRated: { + /** @enum {string} */ + status: "rated"; + rating: external["schemas/playback-podcast.json"]["components"]["schemas"]["LegalAgeRating"]; + }; + LegalAgeBodyExempt: { + /** @enum {string} */ + status: "exempt"; + }; + LegalAge: { + /** + * @description The legal basis for the legal age rating. + * Currently, this is 'LOV2015-02-06-7'. + */ + legalReference: string; + body: + | external["schemas/playback-podcast.json"]["components"]["schemas"]["LegalAgeBodyExempt"] + | external["schemas/playback-podcast.json"]["components"]["schemas"]["LegalAgeBodyRated"]; + }; + ResolvePlayable: { + /** + * @description Uri to resolve to get the media manifest. + * Not sure why this is included when the same link is available in the _links collection. + */ + resolve: string; + }; + PlayablePlaybackMetadataResponse: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-podcast.json"]["components"]["schemas"]["ResolvePlayable"]; + /** @description This is always null when the podcast is playable. */ + nonPlayable: null; + id: external["schemas/playback-podcast.json"]["components"]["schemas"]["PodcastEpisodeId"]; + streamingMode: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableStreamingMode"]; + /** + * Format: ISO8601-duration + * @description Duration of the podcast episode as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + availability: external["schemas/playback-podcast.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-podcast.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-podcast.json"]["components"]["schemas"]["Preplay"]; + displayAspectRatio: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + skipDialogInfo: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + interaction: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + interactionPoints: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + sourceMedium: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + manifests: { + playability: "playable"; + href: string; + name: external["schemas/playback-podcast.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + /** @description Always null, since there is no next link for a podcast. */ + next: null; + /** @description Always null, since there is no next link for a podcast. */ + nextLinks: null; + /** @description When present, clients should report progress using this link. */ + progress: { + playability: "playable"; + href: string; + /** @enum {boolean} */ + templated: true; + }; + /** @description Link to possible recommended next program */ + personalizedNext: { + playability: "playable"; + href: string; + /** @enum {boolean} */ + templated: true; + }; + }; + _embedded: { + playability: "playable"; + manifests: + | external["schemas/playback-podcast.json"]["components"]["schemas"]["EmbeddedPlaybackManifestResource"][] + | null; + /** @description Always null, since there is no next link for a podcast. */ + next: null; + /** @description Embedded data for podcast series */ + podcast: { + playability: "playable"; + _links?: { + playability: "playable"; + podcast?: { + playability: "playable"; + name?: string; + href?: string; + }; + }; + titles?: { + playability: "playable"; + title?: string; + subtitle?: string; + }; + imageUrl?: string; + rssFeed?: string; + episodeCount?: number; + } | null; + /** @description Embedded data for podcast episode */ + podcastEpisode: + | ({ + playability: "playable"; + /** @description Video clip ID for podcast episode */ + clipId?: string | null; + }) + | null; + }; + }; + /** @description Metadata response for non-playable content. */ + NonPlayablePlaybackMetadataResponse: { + /** @enum {string} */ + playability: "nonPlayable"; + /** @description This is always null when the podcast is non-playable. */ + playable: null; + nonPlayable: external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayableElement"]; + id: external["schemas/playback-podcast.json"]["components"]["schemas"]["PodcastEpisodeId"]; + streamingMode: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableStreamingMode"]; + /** + * Format: ISO8601-duration + * @description Duration of the podcast as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + availability: external["schemas/playback-podcast.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-podcast.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-podcast.json"]["components"]["schemas"]["Preplay"]; + interaction: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + interactionPoints: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + skipDialogInfo: external["schemas/playback-podcast.json"]["components"]["schemas"]["NullType"]; + sourceMedium: external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + }; + manifests: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-podcast.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + /** @description When present, clients should report progress using this link. */ + progress?: { + playability: "nonPlayable"; + /** @example /tv/userdata/{userId}/progress/programs/MUHH32001019 */ + href: string; + /** @enum {boolean} */ + templated: true; + /** @enum {string} */ + name?: "progress"; + }; + /** @description Link to possible recommended next program */ + personalizedNext?: { + playability: "nonPlayable"; + href: string; + /** @enum {boolean} */ + templated: true; + }; + /** @description Always null, since there is no next link for a podcast. */ + next: null; + /** @description Always null, since there is no next link for a podcast. */ + nextLinks: null; + nonPlayablePosterLinks?: + external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayablePosterLinkCollection"]; + }; + _embedded: { + playability: "nonPlayable"; + manifests: { + playability: "nonPlayable"; + id: string; + availabilityLabel: + external["schemas/playback-podcast.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-podcast.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }[] | null; + }; + }; + PlaybackMetadataResponse: + | external["schemas/playback-podcast.json"]["components"]["schemas"]["PlayablePlaybackMetadataResponse"] + | external["schemas/playback-podcast.json"]["components"]["schemas"]["NonPlayablePlaybackMetadataResponse"]; + EmbeddedPlaybackManifestResource: { + id: string; + availabilityLabel: external["schemas/playback-podcast.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + self: { + href: string; + name: external["schemas/playback-podcast.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; + }; + $defs: Record; + }; + "schemas/playback-program.json": { + paths: Record; + webhooks: Record; + components: { + schemas: { + /** + * Format: progId + * @example DNRR65002318 + */ + ProgId: string; + /** + * Format: progIdWithAccessibility + * @example DNRR65002318TOLK + */ + ProgIdWithAccessibilityVersion: string; + ManifestProgId: + | external["schemas/playback-program.json"]["components"]["schemas"]["ProgId"] + | external["schemas/playback-program.json"]["components"]["schemas"]["ProgIdWithAccessibilityVersion"]; + HrefLink: { + href: string; + }; + /** @description The current schedule for the live-to-vod recording for a program (such as a live event). Clients should check 'playableNow' or the 'playableAt' timestamp to see if they should fetch the media manifest and start playing the media stream. */ + ProgramScheduleResource: { + _links: { + self: external["schemas/playback-program.json"]["components"]["schemas"]["HrefLink"]; + metadata: external["schemas/playback-program.json"]["components"]["schemas"]["HrefLink"]; + manifest: external["schemas/playback-program.json"]["components"]["schemas"]["HrefLink"]; + }; + /** + * Format: date-time + * @description Timestamp indicating when the schedule was last updated. + */ + scheduleUpdatedAt?: string | null; + /** + * Format: date-time + * @description Timestamp indicating the time according to PSAPI when this response was produced. + */ + currentServerTime: string; + transmission: + external["schemas/playback-program.json"]["components"]["schemas"]["ProgramTransmissionSchedule"]; + /** + * Format: date-time + * @description The point in time when the client should be able to play the media stream. + */ + playableAt: string; + /** @description Whether or not the client may start playing the media stream. */ + playableNow: boolean; + /** @description The number of seconds remaining until the client may start playing the media stream, that is, the number of seconds until the time indicated by 'playableAt'. */ + playableInSeconds: number; + /** @description A message the client can display to the end user if the media stream is not yet playable. */ + endUserMessage?: string; + }; + ProgramTransmissionSchedule: { + /** + * Format: date-time + * @description Start time for the live event. Note that the media stream will *not* be playable at this time, since the live-to-vod stream needs a few segments to be playable. Clients should wait until the time designated by 'playableAt' or check the 'playableNow' property. + */ + start: string; + /** Format: date-time */ + stop: string; + }; + PlaybackManifestResponse: + | external["schemas/playback-program.json"]["components"]["schemas"]["PlayableManifest"] + | external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayableManifest"]; + PlayableManifest: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableElement"]; + statistics: { + playability: "playable"; + scores: external["schemas/playback-program.json"]["components"]["schemas"]["ScoresStatistics"]; + ga: external["schemas/playback-program.json"]["components"]["schemas"]["GoogleAnalytics"]; + conviva: external["schemas/playback-program.json"]["components"]["schemas"]["ConvivaStatistics"]; + luna: external["schemas/playback-program.json"]["components"]["schemas"]["LunaStatistics"]; + qualityOfExperience: external["schemas/playback-program.json"]["components"]["schemas"]["QoEStatistics"]; + snowplow: external["schemas/playback-program.json"]["components"]["schemas"]["SnowPlow"]; + }; + nonPlayable: null; + id: external["schemas/playback-program.json"]["components"]["schemas"]["ManifestProgId"]; + streamingMode: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["schemas/playback-program.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: + | external["schemas/playback-program.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-program.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + sourceMedium: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableSourceMedium"]; + /** @description Hypermedia links to relevant resources. */ + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + metadata: { + playability: "playable"; + href?: string; + /** @enum {string} */ + name?: "metadata"; + }; + }; + }; + NonPlayableManifest: { + /** @enum {string} */ + playability: "nonPlayable"; + /** @description This is always null when the program is non-playable. */ + playable: null; + statistics: { + playability: "nonPlayable"; + /** @description Always null for nonPlayable */ + scores: null; + /** @description Always null for nonPlayable */ + ga: null; + /** @description Always null for nonPlayable */ + luna: null; + /** @description Always null for nonPlayable */ + qualityOfExperience: null; + /** @description Always null for nonPlayable */ + snowplow: null; + }; + nonPlayable: external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayableElement"]; + id: external["schemas/playback-program.json"]["components"]["schemas"]["ManifestProgId"]; + streamingMode: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external["schemas/playback-program.json"]["components"]["schemas"]["Availability"]; + displayAspectRatio: + | external["schemas/playback-program.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-program.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + sourceMedium: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + }; + metadata: { + playability: "nonPlayable"; + href: string; + /** @enum {string} */ + name: "metadata"; + }; + nonPlayablePosterLinks?: + external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayablePosterLinkCollection"]; + }; + }; + /** @description Information that only applies to a playable program. */ + PlayableElement: { + endSequenceStartTime: string | null; + /** + * Format: ISO8601-duration + * @description Duration of the program as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + /** @description List of media assets. Currently only one item will be provided (in HLS format) but in the future this list might be expanded to contain different types of assets. */ + assets: external["schemas/playback-program.json"]["components"]["schemas"]["MediaUrl"][]; + liveBuffer: external["schemas/playback-program.json"]["components"]["schemas"]["LiveBuffer"]; + /** @description List of thumbnails. */ + thumbnails: external["schemas/playback-program.json"]["components"]["schemas"]["ThumbnailsItem"][]; + /** @description Link to thumbnails vtt */ + thumbnails2D?: { + /** @description Url to thumbnails feed. */ + url: string; + }; + /** @description List of subtitles for clients that do side-loading. Subtitles are in the [.vtt format](https://en.wikipedia.org/wiki/WebVTT). This is also true for subtitles linked in the manifest files. */ + subtitles: external["schemas/playback-program.json"]["components"]["schemas"]["Subtitles"][]; + }; + MediaUrl: { + /** @description The media URL itself. */ + url: string; + format: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableFormat"]; + mimeType: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableMimeType"]; + /** @description True if program is marked as HDSecure, false otherwise */ + encrypted: boolean; + }; + /** + * @description The video format for the resource identified by the URL. + * @enum {string} + */ + PlayableFormat: "HLS" | "MP4" | "MP3"; + /** + * @description The mimetype that corresponds to the specified format. + * @enum {string} + */ + PlayableMimeType: "application/mp4" | "application/vnd.apple.mpegurl" | "audio/mp4" | "video/mp4" | "audio/mp3"; + /** @description Information about the live buffer for live2vod programs. Null for on-demand programs. */ + LiveBuffer: + | ({ + /** @description This is always null. The idea was to provide the start point for growing live buffers but the API doesn't have this information. It must be read from the manifest itself. */ + bufferStartTime: string | null; + /** + * Format: ISO8601-duration + * @description The length of the live buffer as an ISO 8601 formatted string. However, buffer duration doesn't really make sense for growing buffers, hence the duration is set to zero (PT0S). + * @example PT0S + */ + bufferDuration: string; + /** + * @description Live2vod programs have a growing live buffer. + * @enum {string} + */ + bufferType: "growing"; + }) + | null; + /** @description Thumbnails item. The thumbnails follow the displayAspectRatio for the channel, which can be used to derive height from width. Only relevant for video channels, null otherwise. */ + ThumbnailsItem: { + /** @description Url to thumbnails feed. */ + url: string; + /** @description Width of the thumbnail. */ + width: number; + }; + Subtitles: { + type: external["schemas/playback-program.json"]["components"]["schemas"]["SubtitleType"]; + language: external["schemas/playback-program.json"]["components"]["schemas"]["SubtitleLanguage"]; + label: external["schemas/playback-program.json"]["components"]["schemas"]["SubtitleLabel"]; + defaultOn: boolean; + webVtt: string; + }; + /** @enum {string} */ + SubtitleType: "gbr" | "mix" | "nor" | "ttv"; + /** @enum {string} */ + SubtitleLanguage: "en" | "nb"; + /** @enum {string} */ + SubtitleLabel: "English" | "Norsk" | "Tekstet for hørselshemmede" | "Norsk – på all tale"; + /** @description This payload should be submitted as-is to Scores. */ + ScoresStatistics: + | ({ + /** + * @description Possible values are 'nrkstream' and 'nrkradio' for production, 'test' for other environments. + * @example nrkstream + */ + springStreamSite: string; + /** + * @description A structured string describing the content. + * Example: 'programspiller/odm/nrk_online/humor/side-om-side/s08e01.gratis-plank.MUHH53000121'. + * @example programspiller/odm/nrk_online/humor/side-om-side/s08e01.gratis-plank.MUHH53000121 + */ + springStreamStream: string; + /** + * @description A structured string describing the client. + * Examples: 'desktop', 'mobil/app/android', 'tv/smart_tv/samsung' etc. + * @example tv/smart_tv/samsung + */ + springStreamContentType: string; + springStreamProgramId: external["schemas/playback-program.json"]["components"]["schemas"]["ManifestProgId"]; + /** + * @description Duration of the video content in seconds. For live2vod, it is the length of the transmission in seconds. + * @example 143 + */ + springStreamDuration: number | null; + }) + | null; + /** + * @description * dimension1: ID. KILDESYSTEM:UNIK_IDENTIFIKATOR + * * dimension2: Tittel + * * dimension3: Publiseringsår. + * * dimension4: Publiseringsmåned. + * * dimension5: Publiseringsdag. [01, 31] + * * dimension10: Kilde-ID + * * dimension21: Serie. Kun relevant for episoder/instalments. + * * dimension22: Episodenummer. Kun relevant for episoder (instalments?). + * * dimension23: Kategori + * * dimension25: Mediatype [video | audio] + * * dimension26: Mediakategori [live | ondemand] + * * dimension29: Platform + * + * [Ekstern doc](https://github.com/nrkno/analytics-docs/blob/master/docs/schema.md) + */ + GoogleAnalytics: { + [key: string]: string; + }; + /** @description DEPRECATED! Should be null! */ + ConvivaStatistics: { + /** @enum {string} */ + playerName?: ""; + /** @enum {string} */ + assetName?: ""; + /** @enum {number} */ + duration?: -1; + /** @enum {string} */ + streamType?: ""; + /** @enum {string} */ + streamUrl?: ""; + custom?: external["schemas/playback-program.json"]["components"]["schemas"]["ConvivaCustomProperties"]; + } | null; + /** @description Only allow empty string since Conviva is not in use. */ + ConvivaCustomProperties: { + /** @enum {string} */ + contentId: ""; + /** @enum {string} */ + series: ""; + /** @enum {string} */ + title: ""; + /** @enum {string} */ + episode: ""; + /** @enum {string} */ + category: ""; + /** @enum {string} */ + mediaType: ""; + /** @enum {string} */ + cdnName: ""; + /** @enum {string} */ + applicationVersion: ""; + /** @enum {string} */ + applicationName: ""; + /** @enum {string} */ + serviceName: ""; + /** @enum {string} */ + contentType: ""; + }; + QoEStatistics: { + /** @description DeviceGroup and Name detected by API */ + clientName: string; + /** @description CdnName for playable URL */ + cdnName: string; + /** @description StreamingFormat string representation */ + streamingFormat: string; + /** @description SegmentLength string representation */ + segmentLength: string; + /** + * @description Asset type, calculated based on API response + * @enum {string} + */ + assetType: "ondemand" | "event"; + /** @description Request correlation Id */ + correlationId: string; + }; + /** @enum {string} */ + SnowPlowSource: "prf"; + SnowPlow: { + source: external["schemas/playback-program.json"]["components"]["schemas"]["SnowPlowSource"]; + }; + /** @description DEPRECATED, but some clients need to find the properties */ + LunaStatistics: { + config: external["schemas/playback-program.json"]["components"]["schemas"]["LunaConfig"]; + data: external["schemas/playback-program.json"]["components"]["schemas"]["LunaPayload"]; + }; + LunaConfig: { + /** @description Dummy-link to Akamai config. */ + beacon: string; + }; + LunaPayload: { + /** @description Should be blank string */ + title: string; + /** @description Should be blank string */ + device: string; + /** @description Should be blank string */ + playerId: string; + /** @description Should be blank string */ + deliveryType: string; + /** @description Should be blank string */ + playerInfo: string; + /** + * @description Name of selected CDN + * @example Telenor-Cdn + */ + cdnName: string; + }; + /** @enum {string} */ + PlayableStreamingMode: "live" | "onDemand"; + /** @description Only applicable for live2vod programs. Will be null for on-demand programs. */ + LiveAvailability: { + /** @enum {string} */ + type?: "transmission"; + /** @description Indicates that the transmission is currently on-going (the time is within the boundaries of the transmission interval). */ + isOngoing?: boolean; + transmissionInterval?: + external["schemas/playback-program.json"]["components"]["schemas"]["LiveTransmissionInterval"]; + } | null; + /** + * @example { + * "information": "", + * "isGeoBlocked": true, + * "onDemand": { + * "from": "2015-10-02T20:30:00+02:00", + * "to": "9999-12-31T00:00:00+01:00", + * "hasRightsNow": true + * }, + * "live": null, + * "externalEmbeddingAllowed": true + * } + */ + Availability: { + /** @description Not in use, always an empty string. Intended to be a standardized text about the availability of the media element. */ + information: string; + /** @description Indicates that the media element may not be played outside the range of Norwegian IP addresses. */ + isGeoBlocked: boolean; + onDemand: external["schemas/playback-program.json"]["components"]["schemas"]["OnDemandUsageRights"]; + live: external["schemas/playback-program.json"]["components"]["schemas"]["LiveAvailability"]; + externalEmbeddingAllowed: boolean; + }; + /** @description Start and end time for a live2vod transmission. It can be degenerate (both from and to are null) in case there are no actual usage rights for the transmission. The 'from' property indicates the point in time at which the stream is assumed to be playable (it corresponds to the the 'playableAt' timestamp from the schedule endpoint). This is to avoid problems with clients being served a stream before it is actually playable, and to avoid padding. Likewise, a delta corresponding to the difference between the actual start of the event and the 'playableAt' timestamp is added to the 'to' property, to make sure that the API doesn't indicate that a 'live-to-vod' stream has become VOD while the buffer is still growing. Summarized, for live-to-vod transmissions: * 'From' <- 'playableAt', where 'playableAt' > 'start of event/recording' * 'To' <- 'end of event/recording' + Δt, where Δt = 'playableAt' - 'start of event/recording'. */ + LiveTransmissionInterval: { + /** Format: date-time */ + from: string | null; + /** Format: date-time */ + to: string | null; + }; + /** + * @description Applies to video programs + * @enum {string} + */ + DisplayAspectRatioVideo: "16:9" | "4:3"; + /** @description Applies to audio programs */ + DisplayAspectRatioAudio: null; + /** @description Only applicable for on-demand media elements. Will be null for live media elements. It can be degenerate (both from and to are null) in case no usage rights exist for the on-demand content. Obviously hasRightsNow will be false in that case. */ + OnDemandUsageRights: + | ({ + /** Format: date-time */ + from?: string | null; + /** Format: date-time */ + to?: string | null; + /** @description Indicates whether or not the usage rights are valid at this point in time. This is true if there are usage rights available, and the current time is within the time interval for the usage rights. */ + hasRightsNow: boolean; + }) + | null; + /** @enum {string} */ + PlayableSourceMedium: "audio" | "video"; + /** @description Information that only applies to a non-playable program. */ + NonPlayableElement: { + reason: external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayableReason"]; + messageType: external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayableMessageType"]; + /** @description Standard message for end user describing why the media element is not playable. */ + endUserMessage: string; + /** @description Message with supplemental info about non-playable status. Should be null whenever userAction is not null. */ + endUserMessageSupplement: string | null; + /** + * @deprecated + * @description Obsolete, help links should be located in the _links property. URL to the help page referred to in the end user message. + */ + helpUrl?: string; + userAction?: external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayableUserAction"]; + /** + * @deprecated + * @description Obsolete, but still expected by some clients. Always null. + */ + availableAbroadUrl?: null; + }; + /** + * @description Indicates an action the user may perform to gain access to blocked content. Currently this only applies to content that has been blocked with message ProgramAvailableWithNorwegianResidentClaim. + * @example { + * "action": "claimResidency", + * "label": "Bekreft med BankID" + * } + */ + NonPlayableUserAction: { + action: external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayableUserActionType"]; + /** @description Text label that the client should use when offering the user an interaction to perform the action. */ + label: string; + } | null; + /** @enum {string} */ + NonPlayableUserActionType: "claimResidency" | "loginAndClaimResidency"; + /** @enum {string} */ + NonPlayableReason: "blocked" | "notransmission" | "unavailable"; + /** @enum {string} */ + NonPlayableMessageType: + | "NoMessage" + | "ProgramIsGeoblocked" + | "ProgramIsInLiveBuffer" + | "ProgramIsNotTranscoded" + | "ProgramRightsHasExpired" + | "ProgramRightsHasNotStarted" + | "ProgramUnavailable" + | "ProgramAvailableWithNorwegianResidentClaim" + | "ProgramExceedsProfileRestrictions"; + /** @enum {string} */ + AvailabilityLabel: "Av" | "Synstolk" | "Tegnspråk"; + SeriesLink: { + href: string; + /** @enum {string} */ + name: "series"; + }; + NextLink: { + href: string; + /** @enum {string} */ + name: "next"; + } | null; + /** + * @example { + * "href": "/playback/schedule/program/NNFA12345678" + * } + */ + LiveToVodScheduleLink: { + href: string; + }; + /** @enum {string} */ + ManifestLinkName: "default" | "synstolk" | "tegntolk"; + /** @enum {string} */ + NextLinkName: "metadata" | "program" | "series"; + /** @description A collection of links that the client may present to the user when access to the media has been blocked. */ + NonPlayablePosterLinkCollection: { + href: string; + title: string; + }[]; + /** + * @deprecated + * @description Obsolete, use InteractionPoints instead. + */ + SkipDialogInfo: { + startIntroInSeconds: number; + endIntroInSeconds: number; + startCreditsInSeconds: number; + startIntro: string; + endIntro: string; + startCredits: string; + } | null; + /** + * @deprecated + * @description Obsolete, use InteractionPoints instead. Once the major clients have started using InteractionPoints, this array will be empty. For now: a list of interaction points the client can use to offer interactions to the user, e.g. to skip intro or jump to the next episode. The points are sorted by start time. Points will not overlap. In principle this array could contain multiple seekToPoints, but in practice it will only contain a single seekToPoint corresponding to the intro sequence. To avoid breaking clients, new seekToPoints will not be added. + * @example [ + * { + * "type": "seekToPoint", + * "startTime": 0, + * "endTime": 10.2, + * "seekToTime": 10.2, + * "displayValue": "Hopp over intro" + * }, + * { + * "type": "nextUpPoint", + * "startTime": 500, + * "endTime": 520 + * } + * ] + */ + Interaction: external["schemas/playback-program.json"]["components"]["schemas"]["InteractionPoint"][] | null; + /** + * @description A collection of interaction points the client can use to offer interactions to the user, e.g. to skip intro, skip recap or jump to the next episode. Points will not overlap. New kinds of interaction points may be added in the future, clients should handle this by ignoring them. + * @example { + * "seekToPoints": [ + * { + * "startTime": 0, + * "endTime": 10.2, + * "seekToTime": 10.2, + * "displayValue": "Hopp over sammendrag", + * "label": "recap", + * "autoSkipInBingeMode": false + * }, + * { + * "startTime": 10.2, + * "endTime": 22, + * "seekToTime": 22, + * "displayValue": "Hopp over intro", + * "label": "intro-sequence", + * "autoSkipInBingeMode": true + * } + * ], + * "nextUpPoint": { + * "startTime": 500, + * "countDownDuration": 9, + * "source": "end-credits-start" + * }, + * "recommendNextPoint": null + * } + */ + InteractionPoints: { + /** @description A list of seekToPoints, sorted by startTime. The points to not overlap. Each seekToPoint has a unique label. New seekToPoints may be added in the future, clients should support this by offering seekTo-interaction for the new points without changes. The same goes for removal of current seekToPoints. */ + seekToPoints?: external["schemas/playback-program.json"]["components"]["schemas"]["SeekToPoint"][]; + nextUpPoint?: external["schemas/playback-program.json"]["components"]["schemas"]["NextUpPoint"]; + recommendNextPoint?: external["schemas/playback-program.json"]["components"]["schemas"]["RecommendNextPoint"]; + } | null; + /** @description A position in the media stream when the client may offer the user an option to interact in some way, e.g. to skip intro or jump to the next episode. */ + InteractionPoint: + | external["schemas/playback-program.json"]["components"]["schemas"]["SeekToInteractionPoint"] + | external["schemas/playback-program.json"]["components"]["schemas"]["NextUpInteractionPoint"]; + /** @description Indicates an interval in which the client may prompt the user to jump to a particular position in the media stream. At 'startTime' the client may display e.g. a button showing the 'displayValue' text. If the user clicks the button, the client should jump to the 'seekToTime'. If the client does nothing, the client should hide the button again at 'endTime'. */ + SeekToInteractionPoint: { + /** @enum {string} */ + type: "seekToPoint"; + /** + * Format: float + * @description On reaching this position, the client may start offering an interaction to the user. + */ + startTime: number; + /** + * Format: float + * @description On reaching this position, the client should dismiss the interaction offered to the user. + */ + endTime: number; + /** + * Format: float + * @description If the user interacts, the client should navigate to this position. + */ + seekToTime: number; + /** + * @description A text to show to the end user during the interaction. The text might change, and clients should not rely on it for semantics (see 'label' property). + * @example Hopp over intro + */ + displayValue: string; + }; + /** @description Indicates an interval in which the client may prompt the user to jump to a particular position in the media stream. At 'startTime' the client may display e.g. a button showing the 'displayValue' text. If the user clicks the button, the client should jump to the 'seekToTime'. If the client does nothing, the client should hide the button again at 'endTime'. */ + SeekToPoint: { + /** + * Format: float + * @description On reaching this position, the client may start offering an interaction to the user. + */ + startTime: number; + /** + * Format: float + * @description On reaching this position, the client should dismiss the interaction offered to the user. + */ + endTime: number; + /** + * Format: float + * @description If the user interacts, the client should navigate to this position. + */ + seekToTime: number; + /** + * @description A text to show to the end user during the interaction. The text might change, and clients should not rely on it for semantics (see 'label' property). + * @example Hopp over intro + */ + displayValue: string; + /** @description A label indicating why the interaction was offered. They are not intended for the end user (see 'displayValue' property). They can be used for analytics and debugging. Current known labels are 'recap' and 'intro-sequence'. New labels may be added. */ + label: string; + /** @description Indicates whether or not the client should automatically skip to the time indicated by 'seekToTime' when the user is binge watching, without prompting the user first. */ + autoSkipInBingeMode: boolean; + }; + /** @description Indicates an point in time at which the client may show a dialog with a countdown before jumping to the next episode. The user may dismiss the dialog, in which case the client should not jump to the next episode. Otherwise, the user may actively jump to the next episode, or simply allow the countdown to reach zero. If there is a next program, this point will always be present in the response. Also, if this point is present (not null), there should be a 'next'-link in the '_links' collection. */ + NextUpPoint: { + /** + * Format: float + * @description On reaching this position, the client may start offering the countdown-to-next dialog to the user. Clients should verify that startTime is in the interval [0, duration of the media stream]. + */ + startTime: number; + /** + * Format: int + * @description The number of seconds for the countdown. If 'startTime' + 'countDownDuration' is greater than the duration of the media stream, clients should wait at the end of the stream until the countdown completes before jumping to the next program. + */ + countDownDuration: number; + /** @description Source of the 'startTime'. Clients should use this value for reporting analytics and debugging. Current known values are 'default' and 'end-credits-start'. Clients should handle potential new values. */ + source: string; + } | null; + /** @description Indicates an point in time at which the client may show a dialog to the viewer about recommending content. If UpNextPoint is not null then this is always null */ + RecommendNextPoint: { + /** + * Format: float + * @description On reaching this position, the client may show dialog to recommend more content to the viewer. Clients should verify that startTime is in the interval [0, duration of the media stream]. + */ + startTime: number; + /** @description Source of the recommend next point. Used for reporting analytics and debugging. Current known values are 'no-next-up-point'. Clients should handle potential new values. */ + source: string; + } | null; + /** @description Indicates an interval in which the client may prompt the user to jump to the next program (whatever that may be in the user's context). At 'startTime' the client may display e.g. a button showing a countdown. If the user clicks the button, the client should jump to the next episode. If the client does nothing, the client should hide the button again at 'endTime'. */ + NextUpInteractionPoint: { + /** @enum {string} */ + type: "nextUpPoint"; + /** + * Format: float + * @description On reaching this position, the client may start offering an interaction to the user. + */ + startTime: number; + /** + * Format: float + * @description On reaching this position, the client should dismiss the interaction offered to the user. + */ + endTime: number; + }; + IndexPoint: { + /** @description Title to display for the index point. */ + title: string; + /** + * Format: ISO8601-duration + * @description Point in time indicated as offset from the start of the program. + */ + startPoint: string; + /** @description List of thumbnails in different sizes */ + thumbnails?: external["schemas/playback-program.json"]["components"]["schemas"]["IndexPointThumbnail"][]; + }; + PosterImage: { + url: string; + pixelWidth: number; + }; + IndexPointThumbnail: { + url: string; + pixelWidth: number; + }; + /** @description Image to show before play */ + Poster: { + /** @description List of images in different sizes */ + images: external["schemas/playback-program.json"]["components"]["schemas"]["PosterImage"][]; + }; + SquarePosterImage: { + url: string; + pixelWidth: number; + }; + /** @description Square image for audio programs to show before play */ + SquarePoster: { + /** @description List of images in different sizes */ + images: external["schemas/playback-program.json"]["components"]["schemas"]["SquarePosterImage"][]; + }; + /** @description Title to show before play */ + Titles: { + title: string; + subtitle: string; + }; + Preplay: { + titles: external["schemas/playback-program.json"]["components"]["schemas"]["Titles"]; + /** @description Description to show before play. */ + description: string; + poster: external["schemas/playback-program.json"]["components"]["schemas"]["Poster"]; + squarePoster?: external["schemas/playback-program.json"]["components"]["schemas"]["SquarePoster"]; + /** @description List of index points. */ + indexPoints: external["schemas/playback-program.json"]["components"]["schemas"]["IndexPoint"][]; + }; + LegalAgeRating: { + code: string; + displayValue: string; + displayAge: string; + }; + LegalAgeBodyRated: { + /** @enum {string} */ + status: "rated"; + rating: external["schemas/playback-program.json"]["components"]["schemas"]["LegalAgeRating"]; + }; + LegalAgeBodyExempt: { + /** @enum {string} */ + status: "exempt"; + }; + LegalAge: { + /** + * @description The legal basis for the legal age rating. + * Currently, this is 'LOV2015-02-06-7'. + */ + legalReference: string; + body: + | external["schemas/playback-program.json"]["components"]["schemas"]["LegalAgeBodyExempt"] + | external["schemas/playback-program.json"]["components"]["schemas"]["LegalAgeBodyRated"]; + }; + ResolvePlayable: { + /** + * @description Uri to resolve to get the media manifest. + * Not sure why this is included when the same link is available in the _links collection. + */ + resolve: string; + }; + PlayablePlaybackMetadataResponse: { + /** @enum {string} */ + playability: "playable"; + playable: external["schemas/playback-program.json"]["components"]["schemas"]["ResolvePlayable"]; + /** @description This is always null when the content is playable. */ + nonPlayable: null; + id: external["schemas/playback-program.json"]["components"]["schemas"]["ProgId"]; + streamingMode: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableStreamingMode"]; + /** + * Format: ISO8601-duration + * @description Duration of the program as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + availability: external["schemas/playback-program.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-program.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-program.json"]["components"]["schemas"]["Preplay"]; + displayAspectRatio: + | external["schemas/playback-program.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-program.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + skipDialogInfo: external["schemas/playback-program.json"]["components"]["schemas"]["SkipDialogInfo"]; + interaction: external["schemas/playback-program.json"]["components"]["schemas"]["Interaction"]; + interactionPoints: external["schemas/playback-program.json"]["components"]["schemas"]["InteractionPoints"]; + sourceMedium: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "playable"; + self: { + playability: "playable"; + href: string; + }; + manifests: { + playability: "playable"; + href: string; + name: external["schemas/playback-program.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + next: external["schemas/playback-program.json"]["components"]["schemas"]["NextLink"]; + nextLinks: { + playability: "playable"; + href: string; + name: external["schemas/playback-program.json"]["components"]["schemas"]["NextLinkName"]; + }[] | null; + series?: external["schemas/playback-program.json"]["components"]["schemas"]["SeriesLink"]; + /** @description When present, clients should report progress using this link. */ + progress?: { + playability: "playable"; + href: string; + /** @enum {boolean} */ + templated: true; + }; + /** @description Link to possible recommended next program */ + personalizedNext?: { + playability: "playable"; + href: string; + /** @enum {boolean} */ + templated: true; + }; + /** @description Link to anbefalings-api. For use see: https://psapi.nrk.no/documentation/redoc/psapi-anbefaling/ */ + recommendNext?: { + playability: "playable"; + /** @description Absolute URL to psapi-anbefaling */ + href: string; + }; + /** @description Does only apply to TV programs */ + fargerik?: { + playability: "playable"; + /** @description Absolute URL to fargerik */ + href?: string; + } | null; + }; + _embedded: { + playability: "playable"; + manifests: + | external["schemas/playback-program.json"]["components"]["schemas"]["EmbeddedPlaybackManifestResource"][] + | null; + next: external["schemas/playback-program.json"]["components"]["schemas"]["EmbeddedPlaybackNextResource"]; + }; + }; + /** @description Metadata response for a non-playable program. */ + NonPlayablePlaybackMetadataResponse: { + /** @enum {string} */ + playability: "nonPlayable"; + /** @description This is always null when the program is non-playable. */ + playable: null; + nonPlayable: external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayableElement"]; + id: external["schemas/playback-program.json"]["components"]["schemas"]["ProgId"]; + streamingMode: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableStreamingMode"]; + /** + * Format: ISO8601-duration + * @description Duration of the program as an ISO 8601 formatted string. + * @example PT3M3S + */ + duration: string; + availability: external["schemas/playback-program.json"]["components"]["schemas"]["Availability"]; + legalAge: external["schemas/playback-program.json"]["components"]["schemas"]["LegalAge"]; + preplay: external["schemas/playback-program.json"]["components"]["schemas"]["Preplay"]; + displayAspectRatio: + | external["schemas/playback-program.json"]["components"]["schemas"]["DisplayAspectRatioVideo"] + | external["schemas/playback-program.json"]["components"]["schemas"]["DisplayAspectRatioAudio"]; + interaction: external["schemas/playback-program.json"]["components"]["schemas"]["Interaction"]; + interactionPoints: external["schemas/playback-program.json"]["components"]["schemas"]["InteractionPoints"]; + skipDialogInfo: external["schemas/playback-program.json"]["components"]["schemas"]["SkipDialogInfo"]; + sourceMedium: external["schemas/playback-program.json"]["components"]["schemas"]["PlayableSourceMedium"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + }; + manifests: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-program.json"]["components"]["schemas"]["ManifestLinkName"]; + }[] | null; + next: external["schemas/playback-program.json"]["components"]["schemas"]["NextLink"]; + nextLinks: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-program.json"]["components"]["schemas"]["NextLinkName"]; + }[] | null; + series?: external["schemas/playback-program.json"]["components"]["schemas"]["SeriesLink"]; + /** @description When present, clients should report progress using this link. */ + progress?: { + playability: "nonPlayable"; + /** @example /tv/userdata/{userId}/progress/programs/MUHH32001019 */ + href: string; + /** @enum {boolean} */ + templated: true; + /** @enum {string} */ + name?: "progress"; + }; + /** @description Link to possible recommended next program */ + personalizedNext?: { + playability: "nonPlayable"; + href: string; + /** @enum {boolean} */ + templated: true; + }; + schedule?: external["schemas/playback-program.json"]["components"]["schemas"]["LiveToVodScheduleLink"]; + nonPlayablePosterLinks?: + external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayablePosterLinkCollection"]; + }; + _embedded: { + playability: "nonPlayable"; + manifests: { + playability: "nonPlayable"; + id: string; + availabilityLabel: + external["schemas/playback-program.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + playability: "nonPlayable"; + self: { + playability: "nonPlayable"; + href: string; + name: external["schemas/playback-program.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }[] | null; + }; + }; + PlaybackMetadataResponse: + | external["schemas/playback-program.json"]["components"]["schemas"]["PlayablePlaybackMetadataResponse"] + | external["schemas/playback-program.json"]["components"]["schemas"]["NonPlayablePlaybackMetadataResponse"]; + EmbeddedPlaybackNextResource: { + /** @description ProgramId for next program */ + id: string; + _links: { + self: { + href: string; + /** @enum {string} */ + name: "next"; + }; + }; + }; + EmbeddedPlaybackManifestResource: { + id: string; + availabilityLabel: external["schemas/playback-program.json"]["components"]["schemas"]["AvailabilityLabel"]; + _links: { + self: { + href: string; + name: external["schemas/playback-program.json"]["components"]["schemas"]["ManifestLinkName"]; + }; + }; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; + }; + $defs: Record; + }; +} + +export interface operations { + /** + * Redirect to the appropriate manifest endpoint based on ID. + * @description This endpoint is for clients that don't know what kind of media they're going to play. It should only be used if absolutely necessary. The endpoint makes an effort to redirect the client to the appropriate playback endpoint based on pattern matching on the ID. This presumes that the domains of IDs for programs, clips, podcasts and channels are disjunct (which they happen to be). + */ + getPlaybackManifestRedirect: { + parameters: { + query?: { + inSuperUniverse?: components["parameters"]["inSuperUniverseParam"]; + preferredBandwidth?: components["parameters"]["preferredBandwidthAllParam"]; + preferredCdn?: components["parameters"]["preferredCdnParam"]; + live2Vod?: components["parameters"]["live2vodParam"]; + "eea-portability"?: components["parameters"]["eea-portabilityParam"]; + }; + path: { + id: components["parameters"]["idParam"]; + }; + }; + responses: { + /** @description Moved Permanently. Implies that one of the pattern matches were successful. */ + 301: { + headers: { + /** @description The appropriate endpoint to call for the ID provided. */ + Location?: string; + }; + content: never; + }; + /** @description Bad request. Implies that all pattern match attempts failed. */ + 400: { + content: never; + }; + }; + }; + /** Playback Manifest for the specified program. */ + getPlaybackProgramManifest: { + parameters: { + query?: { + inSuperUniverse?: components["parameters"]["inSuperUniverseParam"]; + preferredBandwidth?: components["parameters"]["preferredBandwidthProgramParam"]; + preferredCdn?: components["parameters"]["preferredCdnParam"]; + live2Vod?: components["parameters"]["live2vodParam"]; + "eea-portability"?: components["parameters"]["eea-portabilityParam"]; + offline?: components["parameters"]["offlineParam"]; + ageRestriction?: components["parameters"]["ageRestrictionParam"]; + contentGroup?: components["parameters"]["contentGroupParam"]; + hdrlevel?: components["parameters"]["hdrlevelParam"]; + }; + path: { + programId: components["parameters"]["programIdManifestParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-program.json"]["components"]["schemas"]["PlaybackManifestResponse"]; + }; + }; + /** @description Bad request. Invalid ProgramId. */ + 400: { + content: never; + }; + /** @description Forbidden. You are not authorized to access this content. */ + 403: { + content: never; + }; + /** @description Not found. The requested resource could not be found. */ + 404: { + content: never; + }; + /** @description Service unavailable. Program is temporarily unavailable. Please try again after a given number of seconds. Check for a 'Retry-After' header. */ + 503: { + content: never; + }; + }; + }; + /** Playback Manifest for the specified podcast. */ + getPlaybackPodcastManifest: { + parameters: { + path: { + podcastEpisodeId: components["parameters"]["podcastEpisodeIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-podcast.json"]["components"]["schemas"]["PlaybackManifestResponse"]; + }; + }; + }; + }; + /** Playback Manifest for the specified podcast episode. */ + getPlaybackPodcastManifestWithSeriesId: { + parameters: { + path: { + podcastEpisodeId: components["parameters"]["podcastEpisodeIdParam"]; + podcastSeriesId: components["parameters"]["podcastSeriesIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-podcast.json"]["components"]["schemas"]["PlaybackManifestResponse"]; + }; + }; + }; + }; + /** + * Playback Manifest for the specified channel. + * @description [SLO](https://nrkconfluence.atlassian.net/l/c/GdzLGDxy) + */ + getPlaybackChannelManifest: { + parameters: { + query?: { + preferredBandwidth?: components["parameters"]["preferredBandwidthParam"]; + preferredCdn?: components["parameters"]["preferredCdnParam"]; + "eea-portability"?: components["parameters"]["eea-portabilityParam"]; + contentGroup?: components["parameters"]["contentGroupParam"]; + subtitleType?: components["parameters"]["subtitleTypeParam"]; + inSuperUniverse?: components["parameters"]["inSuperUniverseParam"]; + }; + path: { + channelId: components["parameters"]["channelIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-channel.json"]["components"]["schemas"]["PlaybackManifestResponse"]; + }; + }; + /** @description Bad request. The provided channelId is not well-formed. */ + 400: { + content: never; + }; + /** @description Not found. The provided channelId is well-formed, but there is no channel by that ID. */ + 404: { + content: never; + }; + }; + }; + /** Playback Manifest for the specified clip. */ + getPlaybackClipManifest: { + parameters: { + query?: { + preferredBandwidth?: components["parameters"]["preferredBandwidthParam"]; + preferredCdn?: components["parameters"]["preferredCdnParam"]; + }; + path: { + clipId: components["parameters"]["clipIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-clip.json"]["components"]["schemas"]["PlaybackManifestResponse"]; + }; + }; + /** @description Bad request. The provided clipId is not well-formed. */ + 400: { + content: never; + }; + /** @description Not found. Clip not found or published. Check message in response body for details. */ + 404: { + content: never; + }; + /** @description Service unavailable. It may be that the source of the clip data did not respond in a timely manner. Check for a 'Retry-After' header. */ + 503: { + content: never; + }; + }; + }; + /** Playback Manifest for the specified radio clip. */ + getPlaybackNewsClipManifest: { + parameters: { + path: { + radioClipType: components["parameters"]["radioClipTypeParam"]; + radioClipId: components["parameters"]["radioClipIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPlaybackManifestResponse"]; + }; + }; + /** @description Not found. There is no news clip by that ID. */ + 404: { + content: never; + }; + /** @description Service unavailable. */ + 503: { + content: never; + }; + }; + }; + /** + * Redirect to the appropriate metadata endpoint based on ID. + * @description This endpoint is for clients that don't know what kind of media they're going to play. It should only be used if absolutely necessary. The endpoint makes an effort to redirect the client to the appropriate playback endpoint based on pattern matching on the ID. This presumes that the domains of IDs for programs, clips, podcasts and channels are disjunct (which they happen to be). + */ + getPlaybackMetadataRedirect: { + parameters: { + query?: { + live2Vod?: components["parameters"]["live2vodParam"]; + "eea-portability"?: components["parameters"]["eea-portabilityParam"]; + }; + path: { + id: components["parameters"]["idParam"]; + }; + }; + responses: { + /** @description Moved Permanently. Implies that one of the pattern matches were successful. */ + 301: { + headers: { + /** @description The appropriate endpoint to call for the ID provided. */ + Location?: string; + }; + content: never; + }; + /** @description Bad request. Implies that all pattern match attempts failed. */ + 400: { + content: never; + }; + }; + }; + /** + * Playback Metadata for the specified program. + * @description Live-to-VOD: For programs that are not yet playable but has an upcoming live-to-vod transmission and a live-to-vod asset, we link to up-to-date scheduling information by means of the 'schedule' link relation in the '_links' property. + */ + getPlaybackProgramMetadata: { + parameters: { + query?: { + "eea-portability"?: components["parameters"]["eea-portabilityParam"]; + contentGroup?: components["parameters"]["contentGroupParam"]; + offline?: components["parameters"]["offlineParam"]; + ageRestriction?: components["parameters"]["ageRestrictionParam"]; + live2Vod?: components["parameters"]["live2vodParam"]; + }; + path: { + programId: components["parameters"]["programIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-program.json"]["components"]["schemas"]["PlaybackMetadataResponse"]; + }; + }; + /** @description Bad request. Invalid ProgramId. */ + 400: { + content: never; + }; + /** @description Forbidden. You are not authorized to access this content. */ + 403: { + content: never; + }; + /** @description Not found. The requested resource could not be found. */ + 404: { + content: never; + }; + /** @description Service unavailable. Program is temporarily unavailable. Please try again after a given number of seconds. Check for a 'Retry-After' header. */ + 503: { + content: never; + }; + }; + }; + /** Podcast Metadata for the specified podcast episode. */ + getPlaybackPodcastMetadata: { + parameters: { + path: { + podcastEpisodeId: components["parameters"]["podcastEpisodeIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-podcast.json"]["components"]["schemas"]["PlaybackMetadataResponse"]; + }; + }; + }; + }; + /** Podcast Metadata for the specified podcast episode. */ + getPlaybackPodcastMetadataWithSeries: { + parameters: { + path: { + podcastSeriesId: components["parameters"]["podcastSeriesIdParam"]; + podcastEpisodeId: components["parameters"]["podcastEpisodeIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-podcast.json"]["components"]["schemas"]["PlaybackMetadataResponse"]; + }; + }; + }; + }; + /** + * Playback Metadata for the specified channel. + * @description [SLO](https://nrkconfluence.atlassian.net/l/c/GdzLGDxy) + */ + getPlaybackChannelMetadata: { + parameters: { + query?: { + "eea-portability"?: components["parameters"]["eea-portabilityParam"]; + contentGroup?: components["parameters"]["contentGroupParam"]; + }; + path: { + channelId: components["parameters"]["channelIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-channel.json"]["components"]["schemas"]["PlaybackMetadataResponse"]; + }; + }; + /** @description Bad request. The provided channelId is not well-formed. */ + 400: { + content: never; + }; + /** @description Not found. The provided channelId is well-formed, but there is no channel by that ID. */ + 404: { + content: never; + }; + }; + }; + /** Playback Metadata for the specified clip . */ + getPlaybackClipMetadata: { + parameters: { + path: { + clipId: components["parameters"]["clipIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-clip.json"]["components"]["schemas"]["PlaybackMetadataResponse"]; + }; + }; + /** @description Bad request. The provided clipId is not well-formed. */ + 400: { + content: never; + }; + /** @description Not found. Clip not found or published. Check message in response body for details. */ + 404: { + content: never; + }; + /** @description Service unavailable. It may be that the source of the clip data did not respond in a timely manner. Check for a 'Retry-After' header. */ + 503: { + content: never; + }; + }; + }; + /** Playback Metadata for the specified radio clip. */ + getPlaybackNewsClipMetadata: { + parameters: { + path: { + radioClipType: components["parameters"]["radioClipTypeParam"]; + radioClipId: components["parameters"]["radioClipIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-clip.json"]["components"]["schemas"]["RadioClipPlaybackMetadataResponse"]; + }; + }; + /** @description Not found. There is no clip by that ID. */ + 404: { + content: never; + }; + /** @description Service unavailable. */ + 503: { + content: never; + }; + }; + }; + /** + * Schedule information for the specified live-to-vod program. + * @description Polling resource for accurate timing of playback of the live-to-vod recording for the specified program. The purpose is to provide a light-weight response that clients can use while waiting for a live-to-vod stream to be become available for playback. Hence the client will primarily want to check the 'playableNow' flag. Secondarily, if the program isn't yet playable, the 'playableInSeconds' property will indicate when the program is scheduled to be playable. The response is cacheable (sets max-age header). + */ + getPlaybackSchedule: { + parameters: { + path: { + programId: components["parameters"]["programIdParam"]; + }; + }; + responses: { + /** @description Responds with the current schedule for the recording. As the scheduled start time for the recording approaches, the response gets less cacheable. */ + 200: { + headers: { + "Cache-Control"?: + | "public, no-cache" + | "public, max-age=1" + | "public, max-age=5" + | "public, max-age=10" + | "public, max-age=30" + | "public, max-age=60"; + }; + content: { + "application/json": + external["schemas/playback-program.json"]["components"]["schemas"]["ProgramScheduleResource"]; + }; + }; + /** @description Bad request. Invalid ProgramId. */ + 400: { + content: never; + }; + /** @description Either an entirely unknown program, or a program for which no live-to-vod recording is planned. */ + 404: { + content: never; + }; + /** @description Service unavailable. Schedule for program is temporarily unavailable. Please try again after a given number of seconds. Check for a 'Retry-After' header. */ + 503: { + content: never; + }; + }; + }; + /** + * PlugList of linear TV channels. + * @description PlugList of linear TV channels, with links to relevant resources. [SLO](https://nrkconfluence.atlassian.net/l/c/GdzLGDxy) + */ + getTvLivePlugs: { + parameters: { + query?: { + contentGroup?: components["parameters"]["contentGroupParam"]; + regionId?: components["parameters"]["regionIdParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelPlugsOverviewResponseTv"]; + }; + }; + }; + }; + /** + * Overview of linear TV channels. + * @description List of linear TV channels, with links to relevant resources. [SLO](https://nrkconfluence.atlassian.net/l/c/GdzLGDxy) + */ + getTvLive: { + parameters: { + query?: { + contentGroup?: components["parameters"]["contentGroupParam"]; + showStreamingChannels?: components["parameters"]["showStreamingChannelsParam"]; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelOverviewResponseTv"]; + }; + }; + }; + }; + /** + * Overview of linear radio channels. + * @description List of linear radio channels, with links to relevant resources. (Perhaps /radio/linear/channels would have been a better name.) + */ + getRadioLive: { + responses: { + /** @description OK */ + 200: { + content: { + "application/json": + external["schemas/playback-channel.json"]["components"]["schemas"]["ChannelOverviewResponseRadio"]; + }; + }; + }; + }; + /** + * Nielsen statistics for the specified radio channel. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + getNielsenStatisticsForChannel: { + parameters: { + path: { + channelId: components["parameters"]["channelIdParam"]; + }; + }; + responses: { + /** @description Nielsen statistics always reponds with HTTP 410 Gone */ + 410: { + content: never; + }; + }; + }; + /** + * Nielsen statistics for the specified podcast. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + getNielsenStatisticsForPodcast: { + parameters: { + path: { + podcastEpisodeId: components["parameters"]["podcastEpisodeIdParam"]; + }; + }; + responses: { + /** @description Nielsen statistics always reponds with HTTP 410 Gone */ + 410: { + content: never; + }; + }; + }; + /** + * Nielsen statistics for the specified podcast episode. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + getNielsenStatisticsForPodcastWithSeriesId: { + parameters: { + path: { + podcastEpisodeId: components["parameters"]["podcastEpisodeIdParam"]; + podcastSeriesId: components["parameters"]["podcastSeriesIdParam"]; + }; + }; + responses: { + /** @description Nielsen statistics always reponds with HTTP 410 Gone */ + 410: { + content: never; + }; + }; + }; + /** + * Nielsen statistics for the specified on-demand radio program. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + getNielsenStatisticsForProgram: { + parameters: { + path: { + programId: components["parameters"]["programIdParam"]; + }; + }; + responses: { + /** @description Nielsen statistics always reponds with HTTP 410 Gone */ + 410: { + content: never; + }; + }; + }; + /** + * Nielsen statistics for the specified audio clip. + * @deprecated + * @description Resource to fetch payload for Nielsen statistics tracking for audio. + */ + getNielsenStatisticsForClip: { + parameters: { + path: { + clipId: components["parameters"]["clipIdParam"]; + }; + }; + responses: { + /** @description Nielsen statistics always reponds with HTTP 410 Gone */ + 410: { + content: never; + }; + }; + }; +} diff --git a/lib/nrk-search.ts b/lib/nrk-search.ts index 4dcd534..e58ce32 100644 --- a/lib/nrk-search.ts +++ b/lib/nrk-search.ts @@ -5,35 +5,59 @@ export interface paths { "/radio/search/title": { - /** Gives results for titles */ + /** + * Title search + * @description Gives results for titles + */ get: operations["TitleSearch"]; }; "/radio/search/title/suggest": { - /** Gives suggestion for misspelling on titles only */ + /** + * Title suggest search + * @description Gives suggestion for misspelling on titles only + */ get: operations["TitleSuggestSearch"]; }; "/radio/search/suggest": { - /** Deprecated. Use '/radio/search/title/suggest'. Gives suggestion for misspelling */ + /** + * Deprecated + * @deprecated + * @description Deprecated. Use '/radio/search/title/suggest'. Gives suggestion for misspelling + */ get: operations["TitleSuggestSearchDeprecated"]; }; "/radio/search/search": { - /** Search endpoint for all content */ + /** + * Search + * @description Search endpoint for all content + */ get: operations["Search"]; }; "/radio/search/search/suggest": { - /** Gives suggestion for misspelling */ + /** + * Search suggest search + * @description Gives suggestion for misspelling + */ get: operations["RadioSuggestSearch"]; }; "/radio/search/categories/{category}": { - /** Alphabetical listing of all series, podcasts and umbrella seasons that are not excluded from search results in given category. Categories correspond to those in pages-api, including 'alt-innhold'. For the category 'podcast', all podcasts are listed, also those excluded from search results. */ + /** + * List all series, podcast and umbrella seasons in category + * @description Alphabetical listing of all series, podcasts and umbrella seasons that are not excluded from search results in given category. Categories correspond to those in pages-api, including 'alt-innhold'. For the category 'podcast', all podcasts are listed, also those excluded from search results. + */ get: operations["RadioListAllForCategory"]; }; "/radio/search/int/search": { - /** Search endpoint for `internal use only`. Searches series and episodes. */ + /** + * Internal search + * @description Search endpoint for `internal use only`. Searches series and episodes. + */ get: operations["InternalSearch"]; }; } +export type webhooks = Record; + export interface components { schemas: { /** Series search result */ @@ -47,8 +71,6 @@ export interface components { description?: string; trimmedDescription?: string; images?: components["schemas"]["image"][]; - } & { - seriesId: unknown; }; /** Episode search result */ episodesearchintresult: { @@ -57,11 +79,7 @@ export interface components { /** @description If type is `customSeasonEpisode` */ seasonId?: string; /** @enum {string} */ - type: - | "seriesEpisode" - | "podcastEpisode" - | "singleProgram" - | "customSeasonEpisode"; + type: "seriesEpisode" | "podcastEpisode" | "singleProgram" | "customSeasonEpisode"; title: string; subtitle?: string; trimmedSubtitle?: string; @@ -88,8 +106,6 @@ export interface components { /** @description if `isSuggestResult` is true, `suggestQuery` displays the suggested value which is used in the current result set */ suggestQuery?: string; }; - } & { - isSuggestResult: unknown; }; /** * Channel result @@ -131,7 +147,7 @@ export interface components { */ seriesResult: { links?: components["schemas"]["nextLink"]; - results?: { + results?: ({ /** @description Unique id */ id: string; seriesId: string; @@ -145,7 +161,7 @@ export interface components { images: components["schemas"]["image"][]; images_1_1?: components["schemas"]["image"][]; highlights?: components["schemas"]["seriesHighlight"]; - }[]; + })[]; }; /** * Episode results @@ -153,7 +169,7 @@ export interface components { */ episodeResult: { links?: components["schemas"]["nextLink"]; - results?: { + results?: ({ /** @description Unique id */ id: string; episodeId: string; @@ -162,11 +178,7 @@ export interface components { /** @description Only included in type `customSeasonEpisode` */ seasonId?: string; /** @enum {string} */ - type: - | "seriesEpisode" - | "podcastEpisode" - | "customSeasonEpisode" - | "singleProgram"; + type: "seriesEpisode" | "podcastEpisode" | "customSeasonEpisode" | "singleProgram"; title: string; /** @description Not included in type `singleProgram` */ seriesTitle?: string; @@ -175,7 +187,7 @@ export interface components { /** Format: date */ date: string; highlights?: components["schemas"]["episodeHighlight"]; - }[]; + })[]; }; nextLink: { /** @@ -336,30 +348,48 @@ export interface components { totalCount: number; }; }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; } +export type $defs = Record; + +export type external = Record; + export interface operations { - /** Gives results for titles */ + /** + * Title search + * @description Gives results for titles + */ TitleSearch: { parameters: { - header: { - accept?: "application/json;api-version=2"; - }; query: { - /** The query text */ + /** + * @description The query text + * @example stanghelle + */ q: string; - /** Number of results to take */ + /** @description Number of results to take */ take?: number; - /** Number of results to skip */ + /** + * @description Number of results to skip + * @example 0 + */ skip?: number; - /** Filters only playable content */ + /** @description Filters only playable content */ filterNotPlayable?: boolean; - /** Returns only the specified type */ + /** @description Returns only the specified type */ type?: "series" | "episode"; }; + header?: { + accept?: "application/json;api-version=2"; + }; }; responses: { - /** OK */ + /** @description OK */ 200: { content: { "application/json": components["schemas"]["titlesearchresult"]; @@ -367,16 +397,22 @@ export interface operations { }; }; }; - /** Gives suggestion for misspelling on titles only */ + /** + * Title suggest search + * @description Gives suggestion for misspelling on titles only + */ TitleSuggestSearch: { parameters: { query: { - /** The query text */ + /** + * @description The query text + * @example smukker + */ q: string; }; }; responses: { - /** OK */ + /** @description OK */ 200: { content: { "application/json": string[]; @@ -384,18 +420,28 @@ export interface operations { }; }; }; - /** Deprecated. Use '/radio/search/title/suggest'. Gives suggestion for misspelling */ + /** + * Deprecated + * @deprecated + * @description Deprecated. Use '/radio/search/title/suggest'. Gives suggestion for misspelling + */ TitleSuggestSearchDeprecated: { parameters: { query: { - /** The query text */ + /** + * @description The query text + * @example stanghelle + */ q: string; - /** Defaults to 10 if not set */ + /** + * @description Defaults to 10 if not set + * @example 10 + */ take?: number; }; }; responses: { - /** OK */ + /** @description OK */ 200: { content: { "application/json": string[]; @@ -403,26 +449,38 @@ export interface operations { }; }; }; - /** Search endpoint for all content */ + /** + * Search + * @description Search endpoint for all content + */ Search: { parameters: { query: { - /** The query text */ + /** + * @description The query text + * @example stanghelle + */ q: string; - /** Number of results to take per type in the result set (see type parameter) */ + /** @description Number of results to take per type in the result set (see type parameter) */ take?: number; - /** Number of results to skip */ + /** + * @description Number of results to skip + * @example 0 + */ skip?: number; - /** Which page of results to get */ + /** + * @description Which page of results to get + * @example 2 + */ page?: number; - /** Filters only playable content, applies to `episodes` and `series` */ + /** @description Filters only playable content, applies to `episodes` and `series` */ filterNotPlayable?: boolean; - /** Returns only the specified type */ + /** @description Returns only the specified type */ type?: "series" | "episode" | "channel" | "category"; }; }; responses: { - /** OK */ + /** @description OK */ 200: { content: { "application/json": components["schemas"]["searchresult"]; @@ -430,16 +488,22 @@ export interface operations { }; }; }; - /** Gives suggestion for misspelling */ + /** + * Search suggest search + * @description Gives suggestion for misspelling + */ RadioSuggestSearch: { parameters: { query: { - /** The query text */ + /** + * @description The query text + * @example smukker + */ q: string; }; }; responses: { - /** OK */ + /** @description OK */ 200: { content: { "application/json": string[]; @@ -447,25 +511,28 @@ export interface operations { }; }; }; - /** Alphabetical listing of all series, podcasts and umbrella seasons that are not excluded from search results in given category. Categories correspond to those in pages-api, including 'alt-innhold'. For the category 'podcast', all podcasts are listed, also those excluded from search results. */ + /** + * List all series, podcast and umbrella seasons in category + * @description Alphabetical listing of all series, podcasts and umbrella seasons that are not excluded from search results in given category. Categories correspond to those in pages-api, including 'alt-innhold'. For the category 'podcast', all podcasts are listed, also those excluded from search results. + */ RadioListAllForCategory: { parameters: { - path: { - category: string; - }; - query: { + query?: { take?: number; skip?: number; }; + path: { + category: string; + }; }; responses: { - /** OK */ + /** @description OK */ 200: { content: { "application/json": components["schemas"]["categoriesResponse"]; }; }; - /** Category not found */ + /** @description Category not found */ 404: { content: { "text/plain": unknown; @@ -473,28 +540,41 @@ export interface operations { }; }; }; - /** Search endpoint for `internal use only`. Searches series and episodes. */ + /** + * Internal search + * @description Search endpoint for `internal use only`. Searches series and episodes. + */ InternalSearch: { parameters: { query: { - /** The query text */ + /** + * @description The query text + * @example stanghelle + */ q: string; - /** Defaults to 10 if not set */ + /** + * @description Defaults to 10 if not set + * @example 10 + */ take?: number; - /** Defaults to `desc` if not set */ + /** + * @description Defaults to `desc` if not set + * @example asc + */ sort?: string; - /** options `series` or `episode` */ + /** + * @description options `series` or `episode` + * @example series + */ type?: string; }; }; responses: { - /** OK */ + /** @description OK */ 200: { content: { - "application/json": ( - | components["schemas"]["episodesearchintresult"] - | components["schemas"]["seriessearchintresult"] - )[]; + "application/json": + (components["schemas"]["episodesearchintresult"] | components["schemas"]["seriessearchintresult"])[]; }; }; }; diff --git a/lib/nrk.ts b/lib/nrk.ts index c0e121c..da3356d 100644 --- a/lib/nrk.ts +++ b/lib/nrk.ts @@ -1,37 +1,31 @@ import { get, OK } from "https://deno.land/x/kall@v0.1.0/mod.ts"; import { components as searchComponents } from "./nrk-search.ts"; import { components as catalogComponents } from "./nrk-catalog.ts"; +import { external as playbackComponents } from "./nrk-playback.ts"; type ArrayElement = A extends readonly (infer T)[] ? T : never; +type PodcastEpisodes = catalogComponents["schemas"]["EpisodesHalResource"]; +type Podcast = catalogComponents["schemas"]["SeriesHalResource"]; +type PodcastEpisodesSingle = catalogComponents["schemas"]["EpisodeHalResource"]; + export type Serie = catalogComponents["schemas"]["SeriesViewModel"]; -type _OriginalEpisode = catalogComponents["schemas"]["EpisodeHalResource"]; -export type OriginalEpisode = _OriginalEpisode & { url: string }; +export type OriginalEpisode = PodcastEpisodesSingle & { url: string }; +export type PodcastEpisode = catalogComponents["schemas"]["PodcastEpisodeHalResource"]; export type SearchResultList = searchComponents["schemas"]["seriesResult"]["results"]; export type SearchResult = ArrayElement & { description?: string; }; -/* Incomplete manifest types */ -interface Manifest { - id: string; - playable: { - endSequenceStartTime: null; - duration: string; - assets: { - url: string; - format: string; - mimeType: string; - encrypted: boolean; - }[]; - }; -} +type Manifest = playbackComponents["schemas/playback-channel.json"]["components"]["schemas"]["PlayableManifest"]; + +const nrkAPI = `https://psapi.nrk.no`; async function withDownloadLink( - episode: _OriginalEpisode, + episode: PodcastEpisodesSingle, ): Promise { // getting stream link const [playbackStatus, playbackResponse] = await get( - `https://psapi.nrk.no/playback/manifest/podcast/${episode.episodeId}`, + `${nrkAPI}/playback/manifest/podcast/${episode.episodeId}`, ); if (playbackStatus === OK && playbackResponse) { @@ -45,23 +39,22 @@ export const nrkRadio = { search: async (query: string): Promise => { const [status, response] = await get< searchComponents["schemas"]["searchresult"] - >(`https://psapi.nrk.no/radio/search/search?q=${query}`); + >(`${nrkAPI}/radio/search/search?q=${query}`); if (status === OK && response) { return response.results.series?.results; - } else { - throw `Something went wrong with ${query} - got status ${status}`; } + throw `Something went wrong with ${query} - got status ${status}`; }, getSerieData: async (seriesId: string) => { const [ [episodeStatus, episodeResponse], [seriesStatus, serieResponse], ] = await Promise.all([ - get( - `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes`, + get( + `${nrkAPI}/radio/catalog/podcast/${seriesId}/episodes`, ), - get( - `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}`, + get( + `${nrkAPI}/radio/catalog/podcast/${seriesId}`, ), ]); @@ -76,17 +69,15 @@ export const nrkRadio = { ...serieResponse.series, episodes, }; - } else { - throw `Error getting episodes for ${seriesId}: EpisodeStatus: ${episodeStatus}. SerieStatus: ${seriesStatus}`; } + throw `Error getting episodes for ${seriesId}: EpisodeStatus: ${episodeStatus}. SerieStatus: ${seriesStatus}`; }, - getEpisode: async (seriesId: string, episodeId: string): Promise => { - const url = `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`; - const [status, episode] = await get(url); - if (status !== OK) { - throw `Error getting episode ${episodeId}. Status: ${status}. Series: ${seriesId}`; + getEpisode: async (seriesId: string, episodeId: string): Promise => { + const url = `${nrkAPI}/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`; + const [status, episode] = await get(url); + if (status === OK && episode) { + return episode; } - - return episode; + throw `Error getting episode ${episodeId}. Status: ${status}. Series: ${seriesId}`; }, }; diff --git a/routes/api/feeds/[seriesId]/[episodeId]/chapters.ts b/routes/api/feeds/[seriesId]/[episodeId]/chapters.ts index d0f317d..19eee90 100644 --- a/routes/api/feeds/[seriesId]/[episodeId]/chapters.ts +++ b/routes/api/feeds/[seriesId]/[episodeId]/chapters.ts @@ -1,12 +1,19 @@ import { FreshContext } from "$fresh/server.ts"; import { parse, toSeconds } from "https://esm.sh/iso8601-duration@2.1.1"; -import { nrkRadio, OriginalEpisode } from "../../../../../lib/nrk.ts"; +import { nrkRadio, PodcastEpisode } from "../../../../../lib/nrk.ts"; -// TODO: This type `OriginalEpisode` is missing stuff. -function toChapters(episode: OriginalEpisode) { +type Chapter = { + title: string | undefined; + startTime: number | undefined; +}; + +function toChapters(episode: PodcastEpisode): Chapter[] | null { + if (!episode.indexPoints) { + return null; + } return episode.indexPoints.map((indexPoint) => ({ title: indexPoint.title, - startTime: toSeconds(parse(indexPoint.startPoint)), + startTime: indexPoint.startPoint ? toSeconds(parse(indexPoint.startPoint)) : undefined, })); }