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

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

View File

@ -2,8 +2,17 @@ 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"
placeholder="NRK-podcast"
className="border-2 rounded-md px-2"
name="query"
id="query"
defaultValue={props.defaultValue}
/>
<button type="submit" className="my-2 bg-gray-100 rounded-md">
Søk
</button>
</form> </form>
); );
} }

View File

@ -1,15 +1,16 @@
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="" />
<code className="font-mono bg-black text-white select-all p-2">{feedUrl.toString()}</code>
<CopyButton text={feedUrl.toString()}> <CopyButton text={feedUrl.toString()}>
Kopier URL Kopier URL
</CopyButton> </CopyButton>

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;
};
export default function CopyButton({ text, disabled = false, children }: Props) {
const [clicked, setClicked] = useState(false); const [clicked, setClicked] = useState(false);
const startReset = () => { const startReset = () => {
setTimeout(() => { setTimeout(() => {
setClicked(false); setClicked(false);
}, 2_000); }, 2_000);
} };
return ( return (
<button <button
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; href: string;
name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; name: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"ManifestLinkName"
];
}[] }[]
> | null; >
next: Partial< | null;
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NextLink"] next:
> | null; | Partial<
series?: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SeriesLink"]; external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NextLink"]
>
| null;
series?: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SeriesLink"];
progress?: { progress?: {
href: string; href: string;
/** @enum {boolean} */ /** @enum {boolean} */
@ -1468,50 +1636,90 @@ export interface external {
}; };
}; };
_embedded: { _embedded: {
manifests: Partial< manifests:
| Partial<
{ {
id: string; id: string;
availabilityLabel: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityLabel"]; availabilityLabel: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"AvailabilityLabel"
];
_links: { _links: {
self: { self: {
href: string; href: string;
name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; name: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"ManifestLinkName"
];
}; };
}; };
}[] }[]
> | null; >
| null;
}; };
}; };
NonPlayableMetadata: { NonPlayableMetadata: {
/** @enum {string} */ /** @enum {string} */
playability: "nonPlayable"; playability: "nonPlayable";
playable: unknown | null; playable: unknown | null;
nonPlayable: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableElementVm"]; nonPlayable: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NonPlayableElementVm"];
id: string; id: string;
streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"]; streamingMode: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableStreamingMode"];
duration: string | null; duration: string | null;
availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; availability: external[
legalAge: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"]; "https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
preplay: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PreplayVm"]; ]["components"]["schemas"]["AvailabilityVm"];
displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; legalAge: external[
skipDialogInfo: Partial< "https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"] ]["components"]["schemas"]["LegalAgeVm"];
> | null; preplay: external[
sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; "https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PreplayVm"];
displayAspectRatio: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DisplayAspectRatio"];
skipDialogInfo:
| Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SkipDialogInfoVm"]
>
| null;
sourceMedium: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["PlayableSourceMedium"];
_links: { _links: {
self: { self: {
href: string; href: string;
}; };
manifests: Partial< manifests:
| Partial<
{ {
href: string; href: string;
name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; name: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"ManifestLinkName"
];
}[] }[]
> | null; >
next: Partial< | null;
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NextLink"] next:
> | null; | Partial<
series?: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SeriesLink"]; external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NextLink"]
>
| null;
series?: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["SeriesLink"];
progress?: { progress?: {
href: string; href: string;
/** @enum {boolean} */ /** @enum {boolean} */
@ -1519,25 +1727,40 @@ export interface external {
}; };
}; };
_embedded: { _embedded: {
manifests: Partial< manifests:
| Partial<
{ {
id: string; id: string;
availabilityLabel: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityLabel"]; availabilityLabel: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"AvailabilityLabel"
];
_links: { _links: {
self: { self: {
href: string; href: string;
name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; name: external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"][
"ManifestLinkName"
];
}; };
}; };
}[] }[]
> | null; >
| null;
}; };
}; };
MetadataResponse: Partial< MetadataResponse:
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableMetadata"] & Partial<
> & external[
Partial< "https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableMetadata"] ]["components"]["schemas"]["PlayableMetadata"]
>
& Partial<
external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["NonPlayableMetadata"]
>; >;
}; };
}; };
@ -1547,7 +1770,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DebugData"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DebugData"];
}; };
}; };
}; };
@ -1557,7 +1782,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DnsList"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["DnsList"];
}; };
}; };
}; };
@ -1593,7 +1820,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestResponse"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ManifestResponse"];
}; };
}; };
}; };
@ -1623,7 +1852,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestResponse"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ManifestResponse"];
}; };
}; };
}; };
@ -1651,7 +1882,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestResponse"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ManifestResponse"];
}; };
}; };
}; };
@ -1679,7 +1912,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestResponse"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["ManifestResponse"];
}; };
}; };
}; };
@ -1709,7 +1944,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MetadataResponse"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MetadataResponse"];
}; };
}; };
}; };
@ -1729,7 +1966,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MetadataResponse"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MetadataResponse"];
}; };
}; };
}; };
@ -1749,7 +1988,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MetadataResponse"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MetadataResponse"];
}; };
}; };
}; };
@ -1771,7 +2012,9 @@ export interface external {
/** OK */ /** OK */
200: { 200: {
content: { content: {
"application/json": external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MetadataResponse"]; "application/json": external[
"https://psapi.nrk.no/documentation/openapi/playback/openapi.json"
]["components"]["schemas"]["MetadataResponse"];
}; };
}; };
}; };

View File

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

View File

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

View File

@ -1,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,22 +1,23 @@
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",
`${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`,
],
["type", "application/json+chapters"], ["type", "application/json+chapters"],
]), ]),
tag("enclosure", "", [ tag("enclosure", "", [
@ -24,11 +25,11 @@ function toItemTag(seriesId: string, episode: Episode) {
["length", episode.durationInSeconds.toString()], ["length", episode.durationInSeconds.toString()],
["type", "audio/mpeg3"], ["type", "audio/mpeg3"],
]), ]),
]) ]);
} }
async function buildFeed(seriesId: string) { async function buildFeed(seriesId: string) {
const serie = await nrkRadio.getSerieData(seriesId) const serie = await nrkRadio.getSerieData(seriesId);
const imageUrl = serie.squareImage.at(-1)?.url; const imageUrl = serie.squareImage.at(-1)?.url;
const linkValue = `https://radio.nrk.no/podkast/${serie.id}`; const linkValue = `https://radio.nrk.no/podkast/${serie.id}`;
@ -51,18 +52,17 @@ async function buildFeed(seriesId: string) {
podcast feed validation. podcast feed validation.
*/ */
tag("itunes:category", "", [["text", "Government"]]), tag("itunes:category", "", [["text", "Government"]]),
tag("itunes:owner", tag("itunes:owner", [
[
tag("itunes:name", "NRK"), tag("itunes:name", "NRK"),
tag("itunes:email", "nrkpodcast@nrk.no") tag("itunes:email", "nrkpodcast@nrk.no"),
] ]),
),
tag( tag(
"description", "description",
serie.titles.subtitle || "" serie.titles.subtitle || "",
), ),
tag("ttl", "60"), //60 minutes tag("ttl", "60"), //60 minutes
...(imageUrl ? [ ...(imageUrl
? [
tag("itunes:image", "", [ tag("itunes:image", "", [
["href", imageUrl], ["href", imageUrl],
]), ]),
@ -70,9 +70,10 @@ async function buildFeed(seriesId: string) {
tag("url", imageUrl), tag("url", imageUrl),
tag("title", serie.titles.title), tag("title", serie.titles.title),
tag("link", linkValue), tag("link", linkValue),
]) ]),
] : []), ]
...serie.episodes.map(episode => toItemTag(seriesId, episode)), : []),
...serie.episodes.map((episode) => toItemTag(seriesId, episode)),
]), ]),
], ],
[ [
@ -80,18 +81,18 @@ async function buildFeed(seriesId: string) {
["xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd"], ["xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd"],
["xmlns:content", "http://purl.org/rss/1.0/modules/content/"], ["xmlns:content", "http://purl.org/rss/1.0/modules/content/"],
["xmlns:podcast", "https://podcastindex.org/namespace/1.0"], ["xmlns:podcast", "https://podcastindex.org/namespace/1.0"],
] ],
) ),
); );
} }
export const handler = async (req: Request, _ctx: HandlerContext): Promise<Response> => { export const handler = async (_req: Request, ctx: 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; export const handler = async (_req: Request, ctx: FreshContext): Promise<Response> => {
const episodeId = _ctx.params.episodeId; const seriesId = ctx.params.seriesId;
const episodeId = ctx.params.episodeId;
console.log("going to get ep for ", episodeId); console.log("going to get ep for ", episodeId);
const episode = await nrkRadio.getEpisode(seriesId, episodeId); const episode = await nrkRadio.getEpisode(seriesId, episodeId);
if (!episode) {
return new Response(JSON.stringify({ message: `Episode ${episodeId} is missing` }), {
headers: {
"Content-Type": "application/json",
},
status: 500,
});
}
const chapters = toChapters(episode); const chapters = toChapters(episode);
const body = { const body = {
version: "1.2.0", version: "1.2.0",
chapters, chapters,
} };
return new Response(JSON.stringify(body), { return new Response(JSON.stringify(body), {
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json",
} },
}); });
}; };

View File

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

View File

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

View File

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