From 77428d374aab3c36f7f510567f9ce9b7e170597a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20H=C3=A5rek=20Andreassen?= Date: Sun, 24 Mar 2024 13:41:18 +0100 Subject: [PATCH] Upgrade Fresh and add a bunch of small QOL improvements (#16) --- README.md | 11 +- components/Search.tsx | 23 +- components/SeriesCard.tsx | 33 +- deno.json | 32 +- deno.lock | 148 ----- fresh.gen.ts | 25 +- import_map.json | 12 - islands/CopyButton.tsx | 47 +- lib/nrk-catalog.ts | 555 +++++++++++++----- lib/nrk-search.ts | 2 - lib/nrk.ts | 148 ++--- main.ts | 6 - routes/_app.tsx | 16 + routes/api/feeds/[seriesId].ts | 167 +++--- .../feeds/[seriesId]/[episodeId]/chapters.ts | 48 +- routes/index.tsx | 101 ++-- tsconfig.json | 11 - utils.ts | 16 +- 18 files changed, 788 insertions(+), 613 deletions(-) delete mode 100644 deno.lock delete mode 100644 import_map.json create mode 100644 routes/_app.tsx delete mode 100644 tsconfig.json diff --git a/README.md b/README.md index 906778d..49a2ce0 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,21 @@ -# NRSS - RSS Feeds for NRK sin podcaster +# NRSS - RSS Feeds for NRK sin podcaster https://nrss.deno.dev/ ## Local devlopment + install Deno https://deno.land/manual/getting_started/installation ### Run the application -``` shell + +```shell deno task start ``` + 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. diff --git a/components/Search.tsx b/components/Search.tsx index 0fa606b..49841fc 100644 --- a/components/Search.tsx +++ b/components/Search.tsx @@ -1,9 +1,18 @@ export default function Search(props: { defaultValue: string }) { - return ( -
- - - -
- ); + return ( +
+ + + +
+ ); } diff --git a/components/SeriesCard.tsx b/components/SeriesCard.tsx index e19603e..a6cf1b9 100644 --- a/components/SeriesCard.tsx +++ b/components/SeriesCard.tsx @@ -1,19 +1,20 @@ -import { SearchResult } from "../lib/nrk.ts" +import { SearchResult } from "../lib/nrk.ts"; import CopyButton from "../islands/CopyButton.tsx"; -export function SerieCard(props: { serie: SearchResult, origin: string }) { - const feedUrl = new URL(`/api/feeds/${props.serie.seriesId}`, props.origin); - const image = props.serie.images[0]; - return ( -
-
-

{props.serie.title}

-

{props.serie.description}

- - - Kopier URL - -
-
- ); +export default function SeriesCard(props: { serie: SearchResult; origin: string }) { + const feedUrl = new URL(`/api/feeds/${props.serie.seriesId}`, props.origin); + const image = props.serie.images[0]; + return ( +
+
+

{props.serie.title}

+

{props.serie.description}

+ + {feedUrl.toString()} + + Kopier URL + +
+
+ ); } diff --git a/deno.json b/deno.json index 8d9ac33..17eea29 100644 --- a/deno.json +++ b/deno.json @@ -1,10 +1,38 @@ { + "lock": false, "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": { "jsx": "react-jsx", "jsxImportSource": "preact" + }, + "fmt": { + "exclude": [ + "output" + ], + "useTabs": false, + "lineWidth": 120, + "indentWidth": 2, + "proseWrap": "preserve" + }, + "lint": { + "rules": { + "tags": [ + "fresh", + "recommended" + ] + } } } diff --git a/deno.lock b/deno.lock deleted file mode 100644 index c71d9fb..0000000 --- a/deno.lock +++ /dev/null @@ -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" - } -} diff --git a/fresh.gen.ts b/fresh.gen.ts index 4d62744..3eefc84 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -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 is automatically updated during development when running `dev.ts`. -import config from "./deno.json" assert { type: "json" }; -import * as $0 from "./routes/api/feeds/[seriesId].ts"; -import * as $1 from "./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts"; -import * as $2 from "./routes/index.tsx"; -import * as $$0 from "./islands/CopyButton.tsx"; +import * as $_app from "./routes/_app.tsx"; +import * as $api_feeds_seriesId_ from "./routes/api/feeds/[seriesId].ts"; +import * as $api_feeds_seriesId_episodeId_chapters from "./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts"; +import * as $index from "./routes/index.tsx"; +import * as $CopyButton from "./islands/CopyButton.tsx"; +import { type Manifest } from "$fresh/server.ts"; const manifest = { routes: { - "./routes/api/feeds/[seriesId].ts": $0, - "./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts": $1, - "./routes/index.tsx": $2, + "./routes/_app.tsx": $_app, + "./routes/api/feeds/[seriesId].ts": $api_feeds_seriesId_, + "./routes/api/feeds/[seriesId]/[episodeId]/chapters.ts": $api_feeds_seriesId_episodeId_chapters, + "./routes/index.tsx": $index, }, islands: { - "./islands/CopyButton.tsx": $$0, + "./islands/CopyButton.tsx": $CopyButton, }, baseUrl: import.meta.url, - config, -}; +} satisfies Manifest; export default manifest; diff --git a/import_map.json b/import_map.json deleted file mode 100644 index 3a7cf57..0000000 --- a/import_map.json +++ /dev/null @@ -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/" - } -} diff --git a/islands/CopyButton.tsx b/islands/CopyButton.tsx index 82a32da..8acca88 100644 --- a/islands/CopyButton.tsx +++ b/islands/CopyButton.tsx @@ -1,28 +1,33 @@ import Preact from "preact"; -import { useState } from "preact/hooks" +import { useState } from "preact/hooks"; 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 = () => { - setTimeout(() => { - setClicked(false); - }, 2_000); - } + const startReset = () => { + setTimeout(() => { + setClicked(false); + }, 2_000); + }; - return ( - - ); + return ( + + ); } diff --git a/lib/nrk-catalog.ts b/lib/nrk-catalog.ts index aa67f93..889a656 100644 --- a/lib/nrk-catalog.ts +++ b/lib/nrk-catalog.ts @@ -508,7 +508,9 @@ export interface components { /** @description Aggregated info for clips */ ExtraMaterialClipsHalEmbedded: { 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 * @example PT2M0S @@ -517,7 +519,9 @@ export interface components { id: string; /** @enum {undefined} */ 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 */ _links: { self: components["schemas"]["HalLink"]; @@ -886,36 +890,56 @@ export interface external { "https://psapi.nrk.no/documentation/openapi/playback/openapi.json": { paths: { "/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": { - 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}": { /** 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}": { - 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}": { - 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}": { - 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}": { /** 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}": { - 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}": { - 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}": { - 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: { @@ -935,8 +959,12 @@ export interface external { href: string; }; }; - playbackData: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlaybackDataVm"]; - locationData: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LocationDataVm"]; + playbackData: external[ + "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: { maxBitrate: number; @@ -951,9 +979,15 @@ export interface external { isBeta: boolean; originalUa: string; wurflId: string | null; - distributionFormat: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DistributionFormat"]; - deviceGroupName: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DeviceGroupName"]; - playerPlatform: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayerPlatform"]; + distributionFormat: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["DistributionFormat"]; + deviceGroupName: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["DeviceGroupName"]; + playerPlatform: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayerPlatform"]; customManifestParameters: ( | "AudioOnly" | "DolbyOnly" @@ -965,7 +999,9 @@ export interface external { | "NoSignalDiscontinuities" | "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} */ DistributionFormat: @@ -975,7 +1011,12 @@ export interface external { | "ProgressiveDownload" | "ProgressiveMp3"; /** @enum {string} */ - DeviceGroupName: "Desktop" | "Mobile" | "SetTopUnit" | "Tablet" | "Tv"; + DeviceGroupName: + | "Desktop" + | "Mobile" + | "SetTopUnit" + | "Tablet" + | "Tv"; /** @enum {string} */ PlayerPlatform: | "Altibox" @@ -1021,28 +1062,51 @@ export interface external { forwardedFor: string; realIp: string; }; - ManifestResponse: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableManifest"] - > & - Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableManifest"] + ManifestResponse: + & Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayableManifest"] + > + & Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["NonPlayableManifest"] >; PlayableManifest: { /** @enum {string} */ 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: { - scores: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ScoresStatisticsVm"]; - ga: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["GoogleAnalyticsVm"]; - conviva: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaStatisticsVm"]; - luna: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaStatisticsVm"]; + scores: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["ScoresStatisticsVm"]; + ga: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["GoogleAnalyticsVm"]; + conviva: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["ConvivaStatisticsVm"]; + luna: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["LunaStatisticsVm"]; }; nonPlayable: unknown | null; id: string; - streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"]; - availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; - displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; - sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; + streamingMode: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["AvailabilityVm"]; + displayAspectRatio: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["DisplayAspectRatio"]; + sourceMedium: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayableSourceMedium"]; _links: { self: { href: string; @@ -1063,12 +1127,22 @@ export interface external { ga: 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; - streamingMode: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableStreamingMode"]; - availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; - displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; - sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; + streamingMode: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayableStreamingMode"]; + availability: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["AvailabilityVm"]; + displayAspectRatio: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["DisplayAspectRatio"]; + sourceMedium: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayableSourceMedium"]; _links: { self: { href: string; @@ -1084,19 +1158,31 @@ export interface external { endSequenceStartTime: string | null; duration: string | null; /** @description List of mediaAssets */ - assets: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["MediaUrlVm"][]; + assets: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["MediaUrlVm"][]; /** @description Livebuffer if element is live */ - liveBuffer: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LiveBufferVm"] - > | null; + liveBuffer: + | Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["LiveBufferVm"] + > + | null; /** @description List of subtitles */ - subtitles: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitlesVm"][]; + subtitles: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["SubtitlesVm"][]; }; MediaUrlVm: { /** @description The media URL itself. */ url: string; - format: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableFormat"]; - mimeType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableMimeType"]; + format: external[ + "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 */ encrypted: boolean; }; @@ -1120,7 +1206,9 @@ export interface external { bufferStartTime: string | null; /** @description Live buffer length */ bufferDuration: string; - bufferType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LiveBufferType"]; + bufferType: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["LiveBufferType"]; }; /** * @description Type of liveBuffer @@ -1128,9 +1216,15 @@ export interface external { */ LiveBufferType: "fixed" | "growing" | "none" | "sliding"; SubtitlesVm: { - type: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleType"]; - language: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleLanguage"]; - label: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SubtitleLabel"]; + type: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["SubtitleType"]; + language: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["SubtitleLanguage"]; + label: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["SubtitleLabel"]; defaultOn: boolean; webVtt: string; }; @@ -1139,7 +1233,10 @@ export interface external { /** @enum {string} */ SubtitleLanguage: "en" | "nb"; /** @enum {string} */ - SubtitleLabel: "English" | "Norsk tekst" | "Tekstet for hørselshemmede"; + SubtitleLabel: + | "English" + | "Norsk tekst" + | "Tekstet for hørselshemmede"; ScoresStatisticsVm: { springStreamSite: string; springStreamStream: string; @@ -1178,14 +1275,18 @@ export interface external { * When duration is unknown, eg channels: -1 */ duration: number; - streamType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaStreamType"]; + streamType: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["ConvivaStreamType"]; /** * @description The URL from where this video content will be downloaded. For * manifest-based streaming protocols, it should be the URL of the top-level * manifest. */ streamUrl: string; - custom: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaCustomProperties"]; + custom: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["ConvivaCustomProperties"]; }; /** @enum {string} */ ConvivaStreamType: "LIVE" | "VOD"; @@ -1221,7 +1322,9 @@ export interface external { * Example: underholdning */ 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 * Example: Telenor-Cdn @@ -1239,7 +1342,9 @@ export interface external { * Example: tv.nrk.no, nrk.no, nrksuper.no */ serviceName: string; - contentType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ConvivaContentType"]; + contentType: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["ConvivaContentType"]; }; /** * @description MediaType for current content @@ -1257,8 +1362,12 @@ export interface external { | "Podcast" | "Program"; LunaStatisticsVm: { - config: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaConfig"]; - data: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LunaPayload"]; + config: external[ + "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: { beacon: string; @@ -1288,27 +1397,37 @@ export interface external { * @description Only applicable for on-demand media elements. Will be null for live media * elements. */ - onDemand: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["OnDemandUsageRights"] - > | null; + onDemand: + | Partial< + 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 * elements. */ live: - | (Partial<{ + | ( + & Partial<{ /** @enum {string} */ type: "channel"; /** @enum {boolean} */ isOngoing: true; transmissionInterval: unknown | null; - }> & - Partial<{ - /** @enum {string} */ - type: "transmission"; - isOngoing: boolean; - transmissionInterval: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LiveTransmissionIntervalVm"]; - }>) + }> + & Partial<{ + /** @enum {string} */ + type: "transmission"; + isOngoing: boolean; + transmissionInterval: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"][ + "LiveTransmissionIntervalVm" + ]; + }> + ) | null; externalEmbeddingAllowed: boolean; }; @@ -1326,8 +1445,12 @@ export interface external { /** @enum {string} */ PlayableSourceMedium: "audio" | "video"; NonPlayableElementVm: { - reason: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableReason"]; - messageType: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableMessageType"]; + reason: external[ + "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 * playable. @@ -1389,7 +1512,9 @@ export interface external { /** @description Image to show before play */ PosterVm: { /** @description List of images in different sizes */ - images: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PosterImageVm"][]; + images: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PosterImageVm"][]; }; /** @description Title to show before play */ TitlesVm: { @@ -1397,12 +1522,18 @@ export interface external { subtitle: string; }; 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: 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 */ - 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: { code: string; @@ -1412,7 +1543,9 @@ export interface external { LegalAgeBodyRated: { /** @enum {string} */ 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: { /** @enum {string} */ @@ -1420,11 +1553,16 @@ export interface external { }; LegalAgeVm: { legalReference: string; - body: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeBodyExempt"] - > & - Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeBodyRated"] + body: + & Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["LegalAgeBodyExempt"] + > + & Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["LegalAgeBodyRated"] >; }; PlayableMetadataVm: { @@ -1434,33 +1572,63 @@ export interface external { PlayableMetadata: { /** @enum {string} */ 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; 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; - availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; - legalAge: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"]; - preplay: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PreplayVm"]; - displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; - skipDialogInfo: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"] - > | null; - sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; + availability: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["AvailabilityVm"]; + legalAge: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["LegalAgeVm"]; + preplay: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PreplayVm"]; + displayAspectRatio: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["DisplayAspectRatio"]; + skipDialogInfo: + | Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["SkipDialogInfoVm"] + > + | null; + sourceMedium: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayableSourceMedium"]; _links: { self: { href: string; }; - manifests: Partial< - { - href: string; - name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; - }[] - > | null; - next: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NextLink"] - > | null; - series?: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SeriesLink"]; + manifests: + | Partial< + { + href: string; + name: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"][ + "ManifestLinkName" + ]; + }[] + > + | null; + next: + | Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["NextLink"] + > + | null; + series?: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["SeriesLink"]; progress?: { href: string; /** @enum {boolean} */ @@ -1468,50 +1636,90 @@ export interface external { }; }; _embedded: { - manifests: Partial< - { - id: string; - availabilityLabel: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityLabel"]; - _links: { - self: { - href: string; - name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; + manifests: + | Partial< + { + id: string; + availabilityLabel: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"][ + "AvailabilityLabel" + ]; + _links: { + self: { + href: string; + name: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"][ + "ManifestLinkName" + ]; + }; }; - }; - }[] - > | null; + }[] + > + | null; }; }; NonPlayableMetadata: { /** @enum {string} */ playability: "nonPlayable"; 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; - 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; - availability: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityVm"]; - legalAge: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["LegalAgeVm"]; - preplay: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PreplayVm"]; - displayAspectRatio: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["DisplayAspectRatio"]; - skipDialogInfo: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SkipDialogInfoVm"] - > | null; - sourceMedium: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableSourceMedium"]; + availability: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["AvailabilityVm"]; + legalAge: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["LegalAgeVm"]; + preplay: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PreplayVm"]; + displayAspectRatio: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["DisplayAspectRatio"]; + skipDialogInfo: + | Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["SkipDialogInfoVm"] + > + | null; + sourceMedium: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayableSourceMedium"]; _links: { self: { href: string; }; - manifests: Partial< - { - href: string; - name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; - }[] - > | null; - next: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NextLink"] - > | null; - series?: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["SeriesLink"]; + manifests: + | Partial< + { + href: string; + name: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"][ + "ManifestLinkName" + ]; + }[] + > + | null; + next: + | Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["NextLink"] + > + | null; + series?: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["SeriesLink"]; progress?: { href: string; /** @enum {boolean} */ @@ -1519,25 +1727,40 @@ export interface external { }; }; _embedded: { - manifests: Partial< - { - id: string; - availabilityLabel: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["AvailabilityLabel"]; - _links: { - self: { - href: string; - name: external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["ManifestLinkName"]; + manifests: + | Partial< + { + id: string; + availabilityLabel: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"][ + "AvailabilityLabel" + ]; + _links: { + self: { + href: string; + name: external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"][ + "ManifestLinkName" + ]; + }; }; - }; - }[] - > | null; + }[] + > + | null; }; }; - MetadataResponse: Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["PlayableMetadata"] - > & - Partial< - external["https://psapi.nrk.no/documentation/openapi/playback/openapi.json"]["components"]["schemas"]["NonPlayableMetadata"] + MetadataResponse: + & Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["PlayableMetadata"] + > + & Partial< + external[ + "https://psapi.nrk.no/documentation/openapi/playback/openapi.json" + ]["components"]["schemas"]["NonPlayableMetadata"] >; }; }; @@ -1547,7 +1770,9 @@ export interface external { /** OK */ 200: { 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 */ 200: { 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 */ 200: { 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 */ 200: { 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 */ 200: { 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 */ 200: { 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 */ 200: { 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 */ 200: { 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 */ 200: { 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 */ 200: { 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"]; }; }; }; diff --git a/lib/nrk-search.ts b/lib/nrk-search.ts index 07c53c8..4dcd534 100644 --- a/lib/nrk-search.ts +++ b/lib/nrk-search.ts @@ -500,5 +500,3 @@ export interface operations { }; }; } - -export interface external {} diff --git a/lib/nrk.ts b/lib/nrk.ts index 1a97606..c0e121c 100644 --- a/lib/nrk.ts +++ b/lib/nrk.ts @@ -1,82 +1,92 @@ import { get, OK } from "https://deno.land/x/kall@v0.1.0/mod.ts"; -import { components as searchComponents } from './nrk-search.ts'; -import { components as catalogComponents } from './nrk-catalog.ts'; +import { components as searchComponents } from "./nrk-search.ts"; +import { components as catalogComponents } from "./nrk-catalog.ts"; - -type ArrayElement = A extends readonly (infer T)[] ? T : never -export type Serie = catalogComponents["schemas"]["SeriesViewModel"] -type OriginalEpisode = catalogComponents["schemas"]["EpisodeHalResource"]; -export type OriginalEpisode = OriginalEpisode & { url: string; } +type ArrayElement = A extends readonly (infer T)[] ? T : never; +export type Serie = catalogComponents["schemas"]["SeriesViewModel"]; +type _OriginalEpisode = catalogComponents["schemas"]["EpisodeHalResource"]; +export type OriginalEpisode = _OriginalEpisode & { url: string }; export type SearchResultList = searchComponents["schemas"]["seriesResult"]["results"]; -export type SearchResult = ArrayElement & { description?: string } +export type SearchResult = ArrayElement & { + description?: string; +}; /* Incomplete manifest types */ interface Manifest { - id: string; - playable: { - endSequenceStartTime: null, - duration: string; - assets: { - url: string; - format: string; - mimeType: string; - encrypted: boolean; - }[]; - }; + id: string; + playable: { + endSequenceStartTime: null; + duration: string; + assets: { + url: string; + format: string; + mimeType: string; + encrypted: boolean; + }[]; + }; } -async function withDownloadLink(episode: OriginalEpisode): Promise { - // getting stream link - const [playbackStatus, playbackResponse] = await get(`https://psapi.nrk.no/playback/manifest/podcast/${episode.episodeId}`) +async function withDownloadLink( + episode: _OriginalEpisode, +): Promise { + // getting stream link + const [playbackStatus, playbackResponse] = await get( + `https://psapi.nrk.no/playback/manifest/podcast/${episode.episodeId}`, + ); - if (playbackStatus === OK && playbackResponse) { - return { ...episode, url: playbackResponse.playable.assets[0].url }; - } else { - throw `Error getting downloadLink for ${episode.episodeId}, serie: ${episode.originalTitle}. Status: ${playbackStatus}` - } + if (playbackStatus === OK && playbackResponse) { + return { ...episode, url: playbackResponse.playable.assets[0].url }; + } else { + throw `Error getting downloadLink for ${episode.episodeId}, serie: ${episode.originalTitle}. Status: ${playbackStatus}`; + } } export const nrkRadio = { - search: async (query: string): Promise => { - - const [status, response] = await get(`https://psapi.nrk.no/radio/search/search?q=${query}`); - if (status === OK && response) { - return response.results.series?.results; - } else { - throw `Something went wrong with ${query} - got status ${status}` - } - }, - getSerieData: async (seriesId: string) => { - - const [ - [episodeStatus, episodeResponse], - [seriesStatus, serieResponse], - ] = await Promise.all([ - get(`https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes`), - get(`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 => { - - - const url = `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes/${episodeId}` - const [status, episode] = await get(url); - if (status !== OK) { - throw `Error getting episode ${episodeId}. Status: ${status}. Series: ${seriesId}`; - } - - return episode; + search: async (query: string): Promise => { + const [status, response] = await get< + searchComponents["schemas"]["searchresult"] + >(`https://psapi.nrk.no/radio/search/search?q=${query}`); + if (status === OK && response) { + return response.results.series?.results; + } else { + throw `Something went wrong with ${query} - got status ${status}`; } -} + }, + getSerieData: async (seriesId: string) => { + const [ + [episodeStatus, episodeResponse], + [seriesStatus, serieResponse], + ] = await Promise.all([ + get( + `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes`, + ), + get( + `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 => { + const url = `https://psapi.nrk.no/radio/catalog/podcast/${seriesId}/episodes/${episodeId}`; + const [status, episode] = await get(url); + if (status !== OK) { + throw `Error getting episode ${episodeId}. Status: ${status}. Series: ${seriesId}`; + } + + return episode; + }, +}; diff --git a/main.ts b/main.ts index bb00964..27962df 100644 --- a/main.ts +++ b/main.ts @@ -1,9 +1,3 @@ -/// -/// -/// -/// -/// - import { start } from "$fresh/server.ts"; import manifest from "./fresh.gen.ts"; diff --git a/routes/_app.tsx b/routes/_app.tsx new file mode 100644 index 0000000..324f36b --- /dev/null +++ b/routes/_app.tsx @@ -0,0 +1,16 @@ +import { PageProps } from "$fresh/server.ts"; + +export default function App({ Component }: PageProps) { + return ( + + + + + NRSS + + + + + + ); +} diff --git a/routes/api/feeds/[seriesId].ts b/routes/api/feeds/[seriesId].ts index c59fa29..2057c20 100644 --- a/routes/api/feeds/[seriesId].ts +++ b/routes/api/feeds/[seriesId].ts @@ -1,97 +1,98 @@ -import { nrkRadio, Episode } from "../../../lib/nrk.ts" -import { HandlerContext } from "$fresh/server.ts"; - -import { serialize, tag, declaration } from "https://raw.githubusercontent.com/olaven/serialize-xml/v0.4.0/mod.ts"; +import { nrkRadio, OriginalEpisode } from "../../../lib/nrk.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 { getHostName } from "../../../utils.ts"; -function toItemTag(seriesId: string, episode: Episode) { - - const description = episode.titles.subtitle || ""; - return tag("item", [ - tag("title", episode.titles.title), - tag("link", episode._links.share.href), - tag("description", description), - tag("itunes:summary", description), - tag("guid", episode.id, [["isPermaLink", "false"]]), - tag("pubDate", new Date(episode.date).toUTCString()), - tag("itunes:duration", episode.durationInSeconds.toString()), - tag("podcast:chapters", "", [ - ["url", `${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`], - ["type", "application/json+chapters"], - ]), - tag("enclosure", "", [ - ["url", episode.url], - ["length", episode.durationInSeconds.toString()], - ["type", "audio/mpeg3"], - ]), - ]) +function toItemTag(seriesId: string, episode: OriginalEpisode) { + const description = episode.titles.subtitle || ""; + return tag("item", [ + tag("title", episode.titles.title), + tag("link", episode._links.share?.href), + tag("description", description), + tag("itunes:summary", description), + tag("guid", episode.id, [["isPermaLink", "false"]]), + tag("pubDate", new Date(episode.date).toUTCString()), + tag("itunes:duration", episode.durationInSeconds.toString()), + tag("podcast:chapters", "", [ + [ + "url", + `${getHostName()}/api/feeds/${seriesId}/${episode.episodeId}/chapters`, + ], + ["type", "application/json+chapters"], + ]), + tag("enclosure", "", [ + ["url", episode.url], + ["length", episode.durationInSeconds.toString()], + ["type", "audio/mpeg3"], + ]), + ]); } async function buildFeed(seriesId: string) { - const serie = await nrkRadio.getSerieData(seriesId) - const imageUrl = serie.squareImage.at(-1)?.url; - const linkValue = `https://radio.nrk.no/podkast/${serie.id}`; + const serie = await nrkRadio.getSerieData(seriesId); + const imageUrl = serie.squareImage.at(-1)?.url; + const linkValue = `https://radio.nrk.no/podkast/${serie.id}`; - // Quickly adapted from https://raw.githubusercontent.com/olaven/paperpod/1cde9abd3174b26e126aa74fc5a3b63fd078c0fd/packages/converter/src/rss.ts - return serialize( - declaration([ - ["version", "1.0"], - ["encoding", "UTF-8"], - ]), - tag( - "rss", - [ - tag("channel", [ - tag("title", serie.titles.title), - tag("link", linkValue), - tag("itunes:author", "NRK"), - /* serie.category.id does not overlap with Apple's supported categories.. + // Quickly adapted from https://raw.githubusercontent.com/olaven/paperpod/1cde9abd3174b26e126aa74fc5a3b63fd078c0fd/packages/converter/src/rss.ts + return serialize( + declaration([ + ["version", "1.0"], + ["encoding", "UTF-8"], + ]), + tag( + "rss", + [ + tag("channel", [ + tag("title", serie.titles.title), + tag("link", linkValue), + tag("itunes:author", "NRK"), + /* serie.category.id does not overlap with Apple's supported categories.. 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 - podcast feed validation. + podcast feed validation. */ - tag("itunes:category", "", [["text", "Government"]]), - tag("itunes:owner", - [ - tag("itunes:name", "NRK"), - tag("itunes:email", "nrkpodcast@nrk.no") - ] - ), - tag( - "description", - serie.titles.subtitle || "" - ), - tag("ttl", "60"), //60 minutes - ...(imageUrl ? [ - tag("itunes:image", "", [ - ["href", imageUrl], - ]), - tag("image", [ - tag("url", imageUrl), - tag("title", serie.titles.title), - 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"], + tag("itunes:category", "", [["text", "Government"]]), + tag("itunes:owner", [ + tag("itunes:name", "NRK"), + tag("itunes:email", "nrkpodcast@nrk.no"), + ]), + tag( + "description", + serie.titles.subtitle || "", + ), + tag("ttl", "60"), //60 minutes + ...(imageUrl + ? [ + tag("itunes:image", "", [ + ["href", imageUrl], + ]), + tag("image", [ + tag("url", imageUrl), + tag("title", serie.titles.title), + 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"], + ], + ), + ); } -export const handler = async (req: Request, _ctx: HandlerContext): Promise => { - const seriesId = _ctx.params.seriesId; - const feedContent = await buildFeed(seriesId); +export const handler = async (_req: Request, ctx: FreshContext): Promise => { + const seriesId = ctx.params.seriesId; + const feedContent = await buildFeed(seriesId); - return new Response(feedContent, { - headers: { - "Content-Type": "application/xml" - } - }); + return new Response(feedContent, { + headers: { + "Content-Type": "application/xml", + }, + }); }; diff --git a/routes/api/feeds/[seriesId]/[episodeId]/chapters.ts b/routes/api/feeds/[seriesId]/[episodeId]/chapters.ts index 18cd600..d0f317d 100644 --- a/routes/api/feeds/[seriesId]/[episodeId]/chapters.ts +++ b/routes/api/feeds/[seriesId]/[episodeId]/chapters.ts @@ -1,27 +1,39 @@ +import { FreshContext } from "$fresh/server.ts"; import { parse, toSeconds } from "https://esm.sh/iso8601-duration@2.1.1"; import { nrkRadio, OriginalEpisode } from "../../../../../lib/nrk.ts"; +// TODO: This type `OriginalEpisode` is missing stuff. function toChapters(episode: OriginalEpisode) { - return episode.indexPoints.map(indexPoint => ({ - title: indexPoint.title, - startTime: toSeconds(parse(indexPoint.startPoint)) - })) + return episode.indexPoints.map((indexPoint) => ({ + title: indexPoint.title, + startTime: toSeconds(parse(indexPoint.startPoint)), + })); } -export const handler = async (req: Request, _ctx: HandlerContext): Promise => { - const seriesId = _ctx.params.seriesId; - const episodeId = _ctx.params.episodeId; - console.log("going to get ep for ", episodeId); - const episode = await nrkRadio.getEpisode(seriesId, episodeId); - const chapters = toChapters(episode); - const body = { - version: "1.2.0", - chapters, - } +export const handler = async (_req: Request, ctx: FreshContext): Promise => { + const seriesId = ctx.params.seriesId; + const episodeId = ctx.params.episodeId; - return new Response(JSON.stringify(body), { - headers: { - "Content-Type": "application/json" - } + console.log("going to get ep for ", 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 body = { + version: "1.2.0", + chapters, + }; + + return new Response(JSON.stringify(body), { + headers: { + "Content-Type": "application/json", + }, + }); }; diff --git a/routes/index.tsx b/routes/index.tsx index 6772409..9213b35 100644 --- a/routes/index.tsx +++ b/routes/index.tsx @@ -1,49 +1,76 @@ import { Head } from "$fresh/runtime.ts"; import { Handlers, PageProps } from "$fresh/server.ts"; -import Search from "../components/Search.tsx" -import { SerieCard } from "../components/SeriesCard.tsx"; -import { nrkRadio, SearchResult, SearchResultList, Serie } from "../lib/nrk.ts"; +import Search from "../components/Search.tsx"; +import SeriesCard from "../components/SeriesCard.tsx"; +import { nrkRadio, SearchResultList } from "../lib/nrk.ts"; interface HandlerData { - query: string; - result?: SearchResultList; - origin: string; + query: string; + result?: SearchResultList; + origin: string; } export const handler: Handlers = { - async GET(request, ctx) { - const url = new URL(request.url); - const query = url.searchParams.get("query"); - let result: SearchResultList | undefined; - if (query) { - result = await nrkRadio.search(query); - } - return ctx.render({ query: query || "", result, origin: url.origin }); - }, + async GET(request, ctx) { + const url = new URL(request.url); + const query = url.searchParams.get("query"); + let result: SearchResultList | undefined; + if (query) { + result = await nrkRadio.search(query); + } + return ctx.render({ query: query || "", result, origin: url.origin }); + }, }; export default function Home({ data }: PageProps) { - return ( - <> - - NRSS - -
-
-

NRSS

-

NRK-podcast som RSS

-
- - {data.result ? ( -
- {data.result.map(result => )} -
- ) : null} -

Hva er dette?

-

Hei! Denne løsningen er laget som en reaksjon på at statsfinansierte NRK lukker ned innholdet sitt til sin egen app fremfor å bygge oppunder åpne standarder som RSS. Denne siden er laget fort og gæli - den er ustabil og kommer til å krasje :) Sjekk gjerne ut kildekoden.

-

Hvordan bruker jeg dette?

-

Søk på NRK-podcasten du vil høre på. Kopier deretter URL-en under bildet. Lim denne inn i akkurat den podcastspilleren du måtte foretrekke! Se her for en oversikt over hvordan det gjøres i populære podcastspillere.

+ return ( + <> + + NRSS + +
+
+

NRSS

+

NRK-podcast som RSS

+
+ + {data.result + ? ( +
+ {data.result.map((result) => ( + + ))}
- - ); + ) + : null} +

Hva er dette?

+

+ Hei! Denne løsningen er laget som en reaksjon på at statsfinansierte NRK lukker ned innholdet sitt til sin + egen app fremfor å bygge oppunder åpne standarder som RSS. Denne siden er laget fort og gæli - den er ustabil + og kommer til å krasje :) Sjekk gjerne ut{" "} + + kildekoden + . +

+

Hvordan bruker jeg dette?

+

+ Søk på NRK-podcasten du vil høre på. Kopier deretter URL-en under bildet. Lim denne inn i akkurat den + podcastspilleren du måtte foretrekke! Se{" "} + + her + {" "} + for en oversikt over hvordan det gjøres i populære podcastspillere. +

+
+ + ); } diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 0ea9368..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "plugins": [ - { - "name": "typescript-deno-plugin", - "enable": true, // default is `true` - "importmap": "import_map.json" - } - ] - } -} diff --git a/utils.ts b/utils.ts index 5df399d..654e90a 100644 --- a/utils.ts +++ b/utils.ts @@ -1,11 +1,9 @@ export function getHostName() { - const deploymentId = Deno.env.get("DENO_DEPLOYMENT_ID"); - if (deploymentId) { - return `https://nrss-${deploymentId}.deno.dev`; - } else { - // assume env - return 'http://localhost:8000'; - } + const deploymentId = Deno.env.get("DENO_DEPLOYMENT_ID"); + if (deploymentId) { + return `https://nrss-${deploymentId}.deno.dev`; + } else { + // assume env + return "http://localhost:8000"; + } } - -