Upgrade Fresh and add a bunch of small QOL improvements #16

Merged
timharek merged 21 commits from chore-upgrade-fresh into main 2024-03-24 12:41:18 +00:00
18 changed files with 788 additions and 613 deletions

View File

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

View File

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

View File

@ -1,19 +1,20 @@
import { SearchResult } from "../lib/nrk.ts" import { SearchResult } from "../lib/nrk.ts";
import CopyButton from "../islands/CopyButton.tsx"; import CopyButton from "../islands/CopyButton.tsx";
export function SerieCard(props: { serie: SearchResult, origin: string }) { export default function SeriesCard(props: { serie: SearchResult; origin: string }) {
const feedUrl = new URL(`/api/feeds/${props.serie.seriesId}`, props.origin); const feedUrl = new URL(`/api/feeds/${props.serie.seriesId}`, props.origin);
const image = props.serie.images[0]; const image = props.serie.images[0];
return ( return (
<div className="border-2 my-2 p-2"> <div className="border-2 my-2 p-2">
<div className="mx-auto h-full w-full"> <div className="mx-auto h-full w-full space-y-2">
<h2 className="text-xl">{props.serie.title}</h2> <h2 className="text-xl">{props.serie.title}</h2>
<p className="text-md">{props.serie.description}</p> <p className="text-md">{props.serie.description}</p>
<img src={image.uri} width={image.width} /> <img src={image.uri} width={image.width} alt="" />
<CopyButton text={feedUrl.toString()}> <code className="font-mono bg-black text-white select-all p-2">{feedUrl.toString()}</code>
Kopier URL <CopyButton text={feedUrl.toString()}>
</CopyButton> Kopier URL
</div> </CopyButton>
</div > </div>
); </div>
);
} }

View File

@ -1,10 +1,38 @@
{ {
"lock": false,
"tasks": { "tasks": {
"start": "deno run -A --watch=static/,routes/ dev.ts" "start": "deno run -A --watch=static/,routes/ dev.ts",
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx"
},
"imports": {
"$fresh/": "https://deno.land/x/fresh@1.6.8/",
"preact": "https://esm.sh/preact@10.19.6",
"preact/": "https://esm.sh/preact@10.19.6/",
"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",
"twind": "https://esm.sh/twind@0.16.17",
"twind/": "https://esm.sh/twind@0.16.17/"
}, },
"importMap": "./import_map.json",
"compilerOptions": { "compilerOptions": {
"jsx": "react-jsx", "jsx": "react-jsx",
"jsxImportSource": "preact" "jsxImportSource": "preact"
},
"fmt": {
"exclude": [
"output"
],
"useTabs": false,
"lineWidth": 120,
"indentWidth": 2,
"proseWrap": "preserve"
},
"lint": {
"rules": {
"tags": [
"fresh",
"recommended"
]
}
} }
} }

148
deno.lock
View File

@ -1,148 +0,0 @@
{
"version": "2",
"remote": {
"https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
"https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49",
"https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f",
"https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d",
"https://deno.land/std@0.140.0/fs/expand_glob.ts": "0c10130d67c9b02164b03df8e43c6d6defbf8e395cb69d09e84a8586e6d72ac3",
"https://deno.land/std@0.140.0/fs/walk.ts": "117403ccd21fd322febe56ba06053b1ad5064c802170f19b1ea43214088fe95f",
"https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3",
"https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09",
"https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b",
"https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633",
"https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee",
"https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d",
"https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44",
"https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9",
"https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757",
"https://deno.land/std@0.150.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
"https://deno.land/std@0.150.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49",
"https://deno.land/std@0.150.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06",
"https://deno.land/std@0.150.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0",
"https://deno.land/std@0.150.0/async/debounce.ts": "564273ef242bcfcda19a439132f940db8694173abffc159ea34f07d18fc42620",
"https://deno.land/std@0.150.0/async/deferred.ts": "bc18e28108252c9f67dfca2bbc4587c3cbf3aeb6e155f8c864ca8ecff992b98a",
"https://deno.land/std@0.150.0/async/delay.ts": "cbbdf1c87d1aed8edc7bae13592fb3e27e3106e0748f089c263390d4f49e5f6c",
"https://deno.land/std@0.150.0/async/mod.ts": "9852cd8ed897ab2d41a8fbee611d574e97898327db5c19d9d58e41126473f02c",
"https://deno.land/std@0.150.0/async/mux_async_iterator.ts": "5b4aca6781ad0f2e19ccdf1d1a1c092ccd3e00d52050d9c27c772658c8367256",
"https://deno.land/std@0.150.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239",
"https://deno.land/std@0.150.0/async/tee.ts": "bcfae0017ebb718cf4eef9e2420e8675d91cb1bcc0ed9b668681af6e6caad846",
"https://deno.land/std@0.150.0/flags/mod.ts": "594472736e24b2f2afd3451cf7ccd58a21706ce91006478a544fdfa056c69697",
"https://deno.land/std@0.150.0/fs/_util.ts": "2cf50bfb1081c2d5f2efec10ac19abbc2baf478e51cd1b057d0da2f30585b6ba",
"https://deno.land/std@0.150.0/fs/walk.ts": "6ce8d87fbaeda23383e979599ad27f3f94b3e5ff0c0cd976b5fc5c2aa0df7d92",
"https://deno.land/std@0.150.0/http/http_status.ts": "897575a7d6bc2b9123f6a38ecbc0f03d95a532c5d92029315dc9f508e12526b8",
"https://deno.land/std@0.150.0/http/server.ts": "0b0a9f3abfcfecead944b31ee9098a0c11a59b0495bf873ee200eb80e7441483",
"https://deno.land/std@0.150.0/media_types/_util.ts": "ce9b4fc4ba1c447dafab619055e20fd88236ca6bdd7834a21f98bd193c3fbfa1",
"https://deno.land/std@0.150.0/media_types/mod.ts": "2d4b6f32a087029272dc59e0a55ae3cc4d1b27b794ccf528e94b1925795b3118",
"https://deno.land/std@0.150.0/media_types/vendor/mime-db.v1.52.0.ts": "724cee25fa40f1a52d3937d6b4fbbfdd7791ff55e1b7ac08d9319d5632c7f5af",
"https://deno.land/std@0.150.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3",
"https://deno.land/std@0.150.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09",
"https://deno.land/std@0.150.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b",
"https://deno.land/std@0.150.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633",
"https://deno.land/std@0.150.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee",
"https://deno.land/std@0.150.0/path/mod.ts": "4945b430b759b0b3d98f2a278542cbcf95e0ad2bd8eaaed3c67322b306b2b346",
"https://deno.land/std@0.150.0/path/posix.ts": "c1f7afe274290ea0b51da07ee205653b2964bd74909a82deb07b69a6cc383aaa",
"https://deno.land/std@0.150.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9",
"https://deno.land/std@0.150.0/path/win32.ts": "bd7549042e37879c68ff2f8576a25950abbfca1d696d41d82c7bca0b7e6f452c",
"https://deno.land/std@0.150.0/semver/mod.ts": "4a5195fa81b4aede8875a386550a1119f01fb58d74aea899b2cfb136c05a7310",
"https://deno.land/std@0.152.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06",
"https://deno.land/std@0.152.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0",
"https://deno.land/std@0.152.0/async/debounce.ts": "564273ef242bcfcda19a439132f940db8694173abffc159ea34f07d18fc42620",
"https://deno.land/std@0.152.0/async/deferred.ts": "bc18e28108252c9f67dfca2bbc4587c3cbf3aeb6e155f8c864ca8ecff992b98a",
"https://deno.land/std@0.152.0/async/delay.ts": "cbbdf1c87d1aed8edc7bae13592fb3e27e3106e0748f089c263390d4f49e5f6c",
"https://deno.land/std@0.152.0/async/mod.ts": "dd0a8ed4f3984ffabe2fcca7c9f466b7932d57b1864ffee148a5d5388316db6b",
"https://deno.land/std@0.152.0/async/mux_async_iterator.ts": "5b4aca6781ad0f2e19ccdf1d1a1c092ccd3e00d52050d9c27c772658c8367256",
"https://deno.land/std@0.152.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239",
"https://deno.land/std@0.152.0/async/tee.ts": "bcfae0017ebb718cf4eef9e2420e8675d91cb1bcc0ed9b668681af6e6caad846",
"https://deno.land/std@0.152.0/http/server.ts": "0b0a9f3abfcfecead944b31ee9098a0c11a59b0495bf873ee200eb80e7441483",
"https://deno.land/std@0.180.0/datetime/_common.ts": "f5c1cb784c616151a3d8198a4ab29f65b7fe5c20a105d8979bde9558c7b52910",
"https://deno.land/std@0.180.0/datetime/constants.ts": "0e91a7019814e6df5ebb710b09aceb79f52cbc618bb06b34431f4f8b89d79505",
"https://deno.land/std@0.180.0/datetime/day_of_year.ts": "f9b9c8f1958e843fa072510fbb36e455431a3cc74ab8f39757bd0a59f4aaad10",
"https://deno.land/std@0.180.0/datetime/difference.ts": "556a5edb807f36f0ca334f235180650ccf05dfeb94aa2573642ef20314ef101c",
"https://deno.land/std@0.180.0/datetime/format.ts": "416807ca1971c5432bfa7d1e429b7c6360cb81fee598e8510dde6e7b0e819a57",
"https://deno.land/std@0.180.0/datetime/is_leap.ts": "cc74a158e029ffe1dfcbdd911107a6de428ee64585b1071d8fd4e1a997670d30",
"https://deno.land/std@0.180.0/datetime/mod.ts": "858d0d695b3a391a9c5faa561e46813e346feaef65bd6ddadb7ee2e65ac4e5a8",
"https://deno.land/std@0.180.0/datetime/parse.ts": "b0af5e272382e516f34dddf9dd1eda31f1197bdf8ee2ff7d93505073e97bd76b",
"https://deno.land/std@0.180.0/datetime/to_imf.ts": "851d072a1c86c7ec34c753e61b1951ceda50c9da805b170a0ecaea3b024dd266",
"https://deno.land/std@0.180.0/datetime/week_of_year.ts": "00ce4203f1efc69e2c0902ff4a4451a49d8a87a561e941b692f3b5f056f1f441",
"https://deno.land/x/code_block_writer@11.0.3/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5",
"https://deno.land/x/code_block_writer@11.0.3/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff",
"https://deno.land/x/denoflate@1.2.1/mod.ts": "f5628e44b80b3d80ed525afa2ba0f12408e3849db817d47a883b801f9ce69dd6",
"https://deno.land/x/denoflate@1.2.1/pkg/denoflate.js": "b9f9ad9457d3f12f28b1fb35c555f57443427f74decb403113d67364e4f2caf4",
"https://deno.land/x/denoflate@1.2.1/pkg/denoflate_bg.wasm.js": "d581956245407a2115a3d7e8d85a9641c032940a8e810acbd59ca86afd34d44d",
"https://deno.land/x/esbuild@v0.14.51/mod.d.ts": "c142324d0383c39de0d7660cd207a7f7f52c7198a13d7d3281c0d636a070f441",
"https://deno.land/x/esbuild@v0.14.51/mod.js": "7432566c71fac77637822dc230319c7392a2d2fef51204c9d12c956d7093c279",
"https://deno.land/x/esbuild@v0.14.51/wasm.d.ts": "c142324d0383c39de0d7660cd207a7f7f52c7198a13d7d3281c0d636a070f441",
"https://deno.land/x/esbuild@v0.14.51/wasm.js": "afc1b6927543b664af60ce452c4929e5dc2bb9a0f4ed47b446a6431847c7598e",
"https://deno.land/x/esbuild_deno_loader@0.5.2/deps.ts": "bf83c27b7787b2f245fa0bc0b99f5041aa949c000a81c016cfe828d06b476d37",
"https://deno.land/x/esbuild_deno_loader@0.5.2/mod.ts": "bc111a68f323dbdb6edec68dd558ab732b27866d2ef304708872d763387b65d7",
"https://deno.land/x/esbuild_deno_loader@0.5.2/src/deno.ts": "0e83ccabbe2b004389288e38df2031b79eb347df2d139fce9394d8e88a11f259",
"https://deno.land/x/esbuild_deno_loader@0.5.2/src/native_loader.ts": "343854a566cf510cf25144f7c09fc0c1097780a31830305142a075d12bb697ba",
"https://deno.land/x/esbuild_deno_loader@0.5.2/src/portable_loader.ts": "35b6c526eed8c2c781a3256b23c30aa7cce69c0ef1d583c15528663287ba18a3",
"https://deno.land/x/esbuild_deno_loader@0.5.2/src/shared.ts": "b64749cd8c0f6252a11498bd8758ef1220003e46b2c9b68e16da63fd7e92b13a",
"https://deno.land/x/fresh@1.1.4/dev.ts": "a66c7d64be35bcd6a8e12eec9c27ae335044c70363a241f2e36ee776db468622",
"https://deno.land/x/fresh@1.1.4/plugins/twind.ts": "c0570d6010e29ba24ee5f43b9d3f1fe735f7fac76d9a3e680c9896373d669876",
"https://deno.land/x/fresh@1.1.4/plugins/twind/shared.ts": "023e0ffcd66668753b5049edab0de46e6d66194fb6026c679034b9bbf04ad6f3",
"https://deno.land/x/fresh@1.1.4/runtime.ts": "b02ec1e2e32cf73a33d262b7c9dcab9468ce16cd89fd424196c71003698a4ab0",
"https://deno.land/x/fresh@1.1.4/server.ts": "f379c9aad24471a71e58fb887fa57e5cc27ad9df035987eb260541c78df38e84",
"https://deno.land/x/fresh@1.1.4/src/dev/deps.ts": "de5470828c17839c0b52c328e6709f3477740b9800deaf724d6569b64b1d3872",
"https://deno.land/x/fresh@1.1.4/src/dev/error.ts": "21a38d240c00279662e6adde41367f1da0ae7e2836d993f818ea94aabab53e7b",
"https://deno.land/x/fresh@1.1.4/src/dev/mod.ts": "f5836b2eccd0efd7c0a726a121f174a974daefc22058f759f07d4df56c46e978",
"https://deno.land/x/fresh@1.1.4/src/runtime/csp.ts": "9ee900e9b0b786057b1009da5976298c202d1b86d1f1e4d2510bde5f06530ac9",
"https://deno.land/x/fresh@1.1.4/src/runtime/head.ts": "0f9932874497ab6e57ed1ba01d549e843523df4a5d36ef97460e7a43e3132fdc",
"https://deno.land/x/fresh@1.1.4/src/runtime/utils.ts": "8320a874a44bdd5905c7d4b87a0e7a14a6c50a2ed133800e72ae57341e4d4faa",
"https://deno.land/x/fresh@1.1.4/src/server/bundle.ts": "7fb20c084948894b8eca90984ef92c3f6d12a194910ecb9adee21ace9f1607bb",
"https://deno.land/x/fresh@1.1.4/src/server/constants.ts": "ad10dda1bc20c25c2926f6a8cfd79ef4368d70b4b03a645f65c04b3fa7d93a8c",
"https://deno.land/x/fresh@1.1.4/src/server/context.ts": "2773bf0f9b170f888a3135df5646b2cd5492add072eeb8569e73eb3cfcad963e",
"https://deno.land/x/fresh@1.1.4/src/server/default_error_page.ts": "9a1a595a1a2b31c9b724b04db82b8af256285536db272658d831ac9ef1d3d448",
"https://deno.land/x/fresh@1.1.4/src/server/deps.ts": "1b467e4d00109356b06117a9f2e5a21a2b48353a0e573b25cdbbfe7e9cfccb34",
"https://deno.land/x/fresh@1.1.4/src/server/htmlescape.ts": "834ac7d0caa9fc38dffd9b8613fb47aeecd4f22d5d70c51d4b20a310c085835c",
"https://deno.land/x/fresh@1.1.4/src/server/mod.ts": "72d213444334dd2e94c757a0eee0fc486c0919399ea9184d07ad042f34edd00d",
"https://deno.land/x/fresh@1.1.4/src/server/render.ts": "6f50707bd1f6e33ed84bb71ae3b0996d202b953cefc4285f5356524c7b21f01f",
"https://deno.land/x/fresh@1.1.4/src/server/types.ts": "dde992ab4ee635df71a7fc96fe4cd85943c1a9286ea8eb586563d5f5ca154955",
"https://deno.land/x/importmap@0.2.1/_util.ts": "ada9a9618b537e6c0316c048a898352396c882b9f2de38aba18fd3f2950ede89",
"https://deno.land/x/importmap@0.2.1/mod.ts": "ae3d1cd7eabd18c01a4960d57db471126b020f23b37ef14e1359bbb949227ade",
"https://deno.land/x/kall@v0.1.0/mod.ts": "7379f599251db0b86e08d25ed74965d7f00f55491c13ffbb524a4d42bd97f0fe",
"https://deno.land/x/kall@v0.1.0/source/codes.ts": "67f72d771a039f96e4d0ddd23a54b27caf2c28d207dd37a1fe2e66b69bb8a0aa",
"https://deno.land/x/kall@v0.1.0/source/filters.ts": "8fd499ca3e4cb3d7e40963107710fec04d2179771128434bb3e7419b2d204dc7",
"https://deno.land/x/kall@v0.1.0/source/methods.ts": "01f7d90f3509da9a46122f17ae9510d8ed14ec252082d3e61b971ad410b6caf2",
"https://deno.land/x/kall@v0.1.0/source/support.ts": "bb32b6c2274255d95413600e67f8705f53656b5e8bbad4165ab95c442c31543f",
"https://deno.land/x/rutt@0.0.14/mod.ts": "5027b8e8b12acca48b396a25aee74ad7ee94a25c24cda75571d7839cbd41113c",
"https://deno.land/x/serializexml@v0.1.0/mod.ts": "922e83b6897ae004a39e14c726e5f04d3056d58cdd2c206c4cd659e959400f17",
"https://deno.land/x/serializexml@v0.3.2/mod.ts": "eb29afa7c8a03e692086a58abb87047a91fa5e51841c4c757807a58eb04e0730",
"https://deno.land/x/ts_morph@16.0.0/common/DenoRuntime.ts": "537800e840d0994f9055164e11bf33eadf96419246af0d3c453793c3ae67bdb3",
"https://deno.land/x/ts_morph@16.0.0/common/mod.ts": "01985d2ee7da8d1caee318a9d07664774fbee4e31602bc2bb6bb62c3489555ed",
"https://deno.land/x/ts_morph@16.0.0/common/ts_morph_common.d.ts": "39f2ddefd4995e4344236c44c2bf296069149f45ef6f00440b56e7b32cb2b3bd",
"https://deno.land/x/ts_morph@16.0.0/common/ts_morph_common.js": "7d908bf4f416aa96de956dc11ecc83b585bed297e16418d496ca04a3481067e0",
"https://deno.land/x/ts_morph@16.0.0/common/typescript.d.ts": "df7dd83543f14081ca74918d5a80ff60f634f465746cf2aff8924b28bcc3b152",
"https://deno.land/x/ts_morph@16.0.0/common/typescript.js": "5c59651248a4c41b25fa7beee8e0d0d0fab5f439fa72d478e65abd8241aa533c",
"https://deno.land/x/ts_morph@16.0.0/mod.ts": "adba9b82f24865d15d2c78ef6074b9a7457011719056c9928c800f130a617c93",
"https://deno.land/x/ts_morph@16.0.0/ts_morph.d.ts": "38668b0e3780282a56a805425494490b0045d1928bd040c47a94095749dab8c3",
"https://deno.land/x/ts_morph@16.0.0/ts_morph.js": "9fc0f3d6a3997c2df023fabc4e529d2117d214ffd4fd04247ca2f56c4e9cd470",
"https://esm.sh/*preact-render-to-string@5.2.4": "5c965103e5039039a89c5de1f551a690aecd3918afe53b5d967ce5d96d939584",
"https://esm.sh/iso8601-duration@2.1.1": "111246e7e1d01ec7d70bf0f84be58d00d09cb1b88c2136f98d05046aea8376a2",
"https://esm.sh/preact@10.11.0": "e888b244446037c56f1881173fb51d1f5fa7aae5599e6c5154619346a6a5094e",
"https://esm.sh/preact@10.11.0/hooks": "2b8ec155eb8b87501663f074acff1d55a9114fa7d88f0b39da06c940af1ff736",
"https://esm.sh/preact@10.11.0/jsx-runtime": "5c123264f19799ab243211132dded45f6d42d594b5c78dd585f947d07bf20eae",
"https://esm.sh/stable/preact@10.11.0/deno/hooks.js": "32a891b0e7abdfbf30dc7c274dbcbd46f2ea04d6637174451c50c41c12429805",
"https://esm.sh/stable/preact@10.11.0/deno/jsx-runtime.js": "0310ceb4c83643ee994baa3104675f08834300731a838ddecf5078853492e8d3",
"https://esm.sh/stable/preact@10.11.0/deno/preact.js": "08219fcb5b92ccdc709233a070ab22478c91521866c819aea679144847e4e87a",
"https://esm.sh/twind@0.16.17": "519a5a4d20ff4f797ddf747af70a5600b3581c50a6edf38a4d77e8d7b4caff74",
"https://esm.sh/twind@0.16.17/sheets": "5a2b275c294ceffb1b09f2cc367b386172b6ccaa14273dbde473865d4d3f6721",
"https://esm.sh/v111/csstype@3.1.1/index.d.ts": "1c29793071152b207c01ea1954e343be9a44d85234447b2b236acae9e709a383",
"https://esm.sh/v111/iso8601-duration@2.1.1/deno/iso8601-duration.js": "5acc3e5498178f378a2b9e724c6493bd92310f369056293dbc49cc90adb8e3b6",
"https://esm.sh/v111/iso8601-duration@2.1.1/lib/index.d.ts": "0765e891e321959e930ffd832d20e7937eb8b279e1fc9cf15b35b0db0c7b8541",
"https://esm.sh/v111/preact-render-to-string@5.2.4/X-ZS8q/deno/preact-render-to-string.js": "7651121e4bb5ef2a48f840dba3f0c13087293ca2eb825aa23ffea3a87bd463b3",
"https://esm.sh/v111/preact-render-to-string@5.2.4/X-ZS8q/src/index.d.ts": "b1d73703252c8570fdf2952475805f5808ba3511fefbd93a3e7bd8406de7dcd0",
"https://esm.sh/v111/preact@10.11.0/hooks/src/index.d.ts": "5c29febb624fc25d71cb0e125848c9b711e233337a08f7eacfade38fd4c14cc3",
"https://esm.sh/v111/preact@10.11.0/jsx-runtime/src/index.d.ts": "e153460ed2b3fe2ad8b93696ecd48fbf73cd628b0b0ea6692b71804a3af69dfd",
"https://esm.sh/v111/preact@10.11.0/src/index.d.ts": "1a5c331227be54be6515b0c92a469d352834fa413963ae84a39a05a3177111f6",
"https://esm.sh/v111/preact@10.11.0/src/jsx.d.ts": "c423715fd7992b2e1446fea11d2d04e8adbd66c1edca1ce5e85f90e0d26a2eb2",
"https://esm.sh/v111/style-vendorizer@2.2.3/deno/style-vendorizer.js": "4823723c1cd5b34a60b4a1dfdf272a821d367fc472c23aeed6c3bcd7af5b7a7b",
"https://esm.sh/v111/twind@0.16.17/deno/sheets.js": "c504a460f5df2954f7334a821ef3ac9bedfc94dcc3f0dbf60af8d22f000391db",
"https://esm.sh/v111/twind@0.16.17/deno/twind.js": "076dfc344b9507b3f5ee8a20d0b1e6eeb2ea3c6688649026ed0e13e4de158ff2",
"https://esm.sh/v111/twind@0.16.17/sheets/sheets.d.ts": "103ecfb19311b86eb5f7a4899f6a3b1192a84cfb73b6ff9a8b84d78b053e6704",
"https://esm.sh/v111/twind@0.16.17/twind.d.ts": "35492f009a6d8695748d121d0b3b210b0819a663eafc77d68296d3110d6ec27f",
"https://raw.githubusercontent.com/olaven/serialize-xml/v0.4.0/mod.ts": "0d8a42635bd6e0130b248d9bae27ab36a40076cfa7a9be0f743fa51d7f64eb9f"
}
}

View File

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

View File

@ -1,12 +0,0 @@
{
"imports": {
"$fresh/": "https://deno.land/x/fresh@1.1.4/",
"preact": "https://esm.sh/preact@10.11.0",
"preact/": "https://esm.sh/preact@10.11.0/",
"preact-render-to-string": "https://esm.sh/*preact-render-to-string@5.2.4",
"@preact/signals": "https://esm.sh/*@preact/signals@1.0.3",
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.0.1",
"twind": "https://esm.sh/twind@0.16.17",
"twind/": "https://esm.sh/twind@0.16.17/"
}
}

View File

@ -1,28 +1,33 @@
import Preact from "preact"; import Preact from "preact";
import { useState } from "preact/hooks" import { useState } from "preact/hooks";
import { IS_BROWSER } from "$fresh/runtime.ts"; import { IS_BROWSER } from "$fresh/runtime.ts";
export default function CopyButton(props: { textBefore: string, textAfter: string } & Preact.PropsWithChildren) { type Props = {
text: string;
children: Preact.ComponentChildren;
disabled?: boolean;
};
const [clicked, setClicked] = useState(false); export default function CopyButton({ text, disabled = false, children }: Props) {
const [clicked, setClicked] = useState(false);
const startReset = () => { const startReset = () => {
setTimeout(() => { setTimeout(() => {
setClicked(false); setClicked(false);
}, 2_000); }, 2_000);
} };
return ( return (
<button <button
onClick={() => { onClick={() => {
navigator.clipboard.writeText(props.text.toString()); navigator.clipboard.writeText(text.toString());
setClicked(true); setClicked(true);
startReset(); startReset();
}} }}
disabled={!IS_BROWSER || props.disabled} disabled={!IS_BROWSER || disabled}
class="px-2 py-1 border(gray-100 2) hover:bg-gray-200 mx-auto" class="px-2 py-1 border(gray-100 2) hover:bg-gray-200 mx-auto"
> >
{clicked ? "Kopiert!" : props.children} {clicked ? "Kopiert!" : children}
</button> </button>
); );
} }

View File

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

View File

@ -500,5 +500,3 @@ export interface operations {
}; };
}; };
} }
export interface external {}

View File

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

View File

@ -1,9 +1,3 @@
/// <reference no-default-lib="true" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
/// <reference lib="deno.ns" />
import { start } from "$fresh/server.ts"; import { start } from "$fresh/server.ts";
import manifest from "./fresh.gen.ts"; import manifest from "./fresh.gen.ts";

16
routes/_app.tsx Normal file
View File

@ -0,0 +1,16 @@
import { PageProps } from "$fresh/server.ts";
export default function App({ Component }: PageProps) {
return (
<html lang="no">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NRSS</title>
</head>
<body>
<Component />
</body>
</html>
);
}

View File

@ -1,97 +1,98 @@
import { nrkRadio, Episode } from "../../../lib/nrk.ts" import { nrkRadio, OriginalEpisode } from "../../../lib/nrk.ts";
import { HandlerContext } from "$fresh/server.ts"; import { FreshContext } from "$fresh/server.ts";
import { declaration, serialize, tag } from "https://raw.githubusercontent.com/olaven/serialize-xml/v0.4.0/mod.ts";
import { serialize, tag, declaration } from "https://raw.githubusercontent.com/olaven/serialize-xml/v0.4.0/mod.ts";
import { getHostName } from "../../../utils.ts"; import { getHostName } from "../../../utils.ts";
function toItemTag(seriesId: string, episode: Episode) { function toItemTag(seriesId: string, episode: OriginalEpisode) {
const description = episode.titles.subtitle || "";
const description = episode.titles.subtitle || ""; return tag("item", [
return tag("item", [ tag("title", episode.titles.title),
tag("title", episode.titles.title), tag("link", episode._links.share?.href),
tag("link", episode._links.share.href), tag("description", description),
tag("description", description), tag("itunes:summary", description),
tag("itunes:summary", description), tag("guid", episode.id, [["isPermaLink", "false"]]),
tag("guid", episode.id, [["isPermaLink", "false"]]), tag("pubDate", new Date(episode.date).toUTCString()),
tag("pubDate", new Date(episode.date).toUTCString()), tag("itunes:duration", episode.durationInSeconds.toString()),
tag("itunes:duration", episode.durationInSeconds.toString()), tag("podcast:chapters", "", [
tag("podcast:chapters", "", [ [
["url", `${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`], "url",
["type", "application/json+chapters"], `${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`,
]), ],
tag("enclosure", "", [ ["type", "application/json+chapters"],
["url", episode.url], ]),
["length", episode.durationInSeconds.toString()], tag("enclosure", "", [
["type", "audio/mpeg3"], ["url", episode.url],
]), ["length", episode.durationInSeconds.toString()],
]) ["type", "audio/mpeg3"],
]),
]);
} }
async function buildFeed(seriesId: string) { async function buildFeed(seriesId: string) {
const serie = await nrkRadio.getSerieData(seriesId) const serie = await nrkRadio.getSerieData(seriesId);
const imageUrl = serie.squareImage.at(-1)?.url; const imageUrl = serie.squareImage.at(-1)?.url;
const linkValue = `https://radio.nrk.no/podkast/${serie.id}`; const linkValue = `https://radio.nrk.no/podkast/${serie.id}`;
// Quickly adapted from https://raw.githubusercontent.com/olaven/paperpod/1cde9abd3174b26e126aa74fc5a3b63fd078c0fd/packages/converter/src/rss.ts // Quickly adapted from https://raw.githubusercontent.com/olaven/paperpod/1cde9abd3174b26e126aa74fc5a3b63fd078c0fd/packages/converter/src/rss.ts
return serialize( return serialize(
declaration([ declaration([
["version", "1.0"], ["version", "1.0"],
["encoding", "UTF-8"], ["encoding", "UTF-8"],
]), ]),
tag( tag(
"rss", "rss",
[ [
tag("channel", [ tag("channel", [
tag("title", serie.titles.title), tag("title", serie.titles.title),
tag("link", linkValue), tag("link", linkValue),
tag("itunes:author", "NRK"), tag("itunes:author", "NRK"),
/* serie.category.id does not overlap with Apple's supported categories.. /* serie.category.id does not overlap with Apple's supported categories..
These podcast feeds are not going to be indexed in itunes anyways, so These podcast feeds are not going to be indexed in itunes anyways, so
a static, valid category is fine. The point is simply to pass third party a static, valid category is fine. The point is simply to pass third party
podcast feed validation. podcast feed validation.
*/ */
tag("itunes:category", "", [["text", "Government"]]), tag("itunes:category", "", [["text", "Government"]]),
tag("itunes:owner", tag("itunes:owner", [
[ tag("itunes:name", "NRK"),
tag("itunes:name", "NRK"), tag("itunes:email", "nrkpodcast@nrk.no"),
tag("itunes:email", "nrkpodcast@nrk.no") ]),
] tag(
), "description",
tag( serie.titles.subtitle || "",
"description", ),
serie.titles.subtitle || "" tag("ttl", "60"), //60 minutes
), ...(imageUrl
tag("ttl", "60"), //60 minutes ? [
...(imageUrl ? [ tag("itunes:image", "", [
tag("itunes:image", "", [ ["href", imageUrl],
["href", imageUrl], ]),
]), tag("image", [
tag("image", [ tag("url", imageUrl),
tag("url", imageUrl), tag("title", serie.titles.title),
tag("title", serie.titles.title), tag("link", linkValue),
tag("link", linkValue), ]),
])
] : []),
...serie.episodes.map(episode => toItemTag(seriesId, episode)),
]),
],
[
["version", "2.0"],
["xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd"],
["xmlns:content", "http://purl.org/rss/1.0/modules/content/"],
["xmlns:podcast", "https://podcastindex.org/namespace/1.0"],
] ]
) : []),
); ...serie.episodes.map((episode) => toItemTag(seriesId, episode)),
]),
],
[
["version", "2.0"],
["xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd"],
["xmlns:content", "http://purl.org/rss/1.0/modules/content/"],
["xmlns:podcast", "https://podcastindex.org/namespace/1.0"],
],
),
);
} }
export const handler = async (req: Request, _ctx: HandlerContext): Promise<Response> => { export const handler = async (_req: Request, ctx: FreshContext): Promise<Response> => {
const seriesId = _ctx.params.seriesId; const seriesId = ctx.params.seriesId;
const feedContent = await buildFeed(seriesId); const feedContent = await buildFeed(seriesId);
return new Response(feedContent, { return new Response(feedContent, {
headers: { headers: {
"Content-Type": "application/xml" "Content-Type": "application/xml",
} },
}); });
}; };

View File

@ -1,27 +1,39 @@
import { FreshContext } from "$fresh/server.ts";
import { parse, toSeconds } from "https://esm.sh/iso8601-duration@2.1.1"; import { parse, toSeconds } from "https://esm.sh/iso8601-duration@2.1.1";
import { nrkRadio, OriginalEpisode } from "../../../../../lib/nrk.ts"; import { nrkRadio, OriginalEpisode } from "../../../../../lib/nrk.ts";
// TODO: This type `OriginalEpisode` is missing stuff.
function toChapters(episode: OriginalEpisode) { function toChapters(episode: OriginalEpisode) {
return episode.indexPoints.map(indexPoint => ({ return episode.indexPoints.map((indexPoint) => ({
title: indexPoint.title, title: indexPoint.title,
startTime: toSeconds(parse(indexPoint.startPoint)) startTime: toSeconds(parse(indexPoint.startPoint)),
})) }));
} }
export const handler = async (req: Request, _ctx: HandlerContext): Promise<Response> => {
const seriesId = _ctx.params.seriesId;
const episodeId = _ctx.params.episodeId;
console.log("going to get ep for ", episodeId); export const handler = async (_req: Request, ctx: FreshContext): Promise<Response> => {
const episode = await nrkRadio.getEpisode(seriesId, episodeId); const seriesId = ctx.params.seriesId;
const chapters = toChapters(episode); const episodeId = ctx.params.episodeId;
const body = {
version: "1.2.0",
chapters,
}
return new Response(JSON.stringify(body), { console.log("going to get ep for ", episodeId);
headers: { const episode = await nrkRadio.getEpisode(seriesId, episodeId);
"Content-Type": "application/json"
} if (!episode) {
return new Response(JSON.stringify({ message: `Episode ${episodeId} is missing` }), {
headers: {
"Content-Type": "application/json",
},
status: 500,
}); });
}
const chapters = toChapters(episode);
const body = {
version: "1.2.0",
chapters,
};
return new Response(JSON.stringify(body), {
headers: {
"Content-Type": "application/json",
},
});
}; };

View File

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

View File

@ -1,11 +0,0 @@
{
"compilerOptions": {
"plugins": [
{
"name": "typescript-deno-plugin",
"enable": true, // default is `true`
"importmap": "import_map.json"
}
]
}
}

View File

@ -1,11 +1,9 @@
export function getHostName() { export function getHostName() {
const deploymentId = Deno.env.get("DENO_DEPLOYMENT_ID"); const deploymentId = Deno.env.get("DENO_DEPLOYMENT_ID");
if (deploymentId) { if (deploymentId) {
return `https://nrss-${deploymentId}.deno.dev`; return `https://nrss-${deploymentId}.deno.dev`;
} else { } else {
// assume env // assume env
return 'http://localhost:8000'; return "http://localhost:8000";
} }
} }