[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto skywatched.app
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #73 from flo-bit/alpha-v1

add og images+tags, add feed to homepage, make urls human readable

authored by

Florian and committed by
GitHub
(Dec 22, 2024, 6:56 PM +0100) 8b86de2b 890bd6c0

+600 -54
+380 -11
package-lock.json
··· 12 12 "@atproto/lex-cli": "^0.5.3", 13 13 "@atproto/oauth-client-node": "^0.2.4", 14 14 "@atproto/xrpc-server": "^0.7.4", 15 + "@ethercorps/sveltekit-og": "^4.0.0-next.1", 15 16 "@libsql/client": "^0.9.0", 16 17 "@node-rs/argon2": "^1.1.0", 17 18 "@oslojs/crypto": "^1.0.1", ··· 1363 1364 "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1364 1365 } 1365 1366 }, 1367 + "node_modules/@ethercorps/svelte-h2j": { 1368 + "version": "5.0.0-next.2", 1369 + "resolved": "https://registry.npmjs.org/@ethercorps/svelte-h2j/-/svelte-h2j-5.0.0-next.2.tgz", 1370 + "integrity": "sha512-0kbk3+oe3Wgcz7WEcNKGHjBqMXi4UfPsgqqvxzQVmSOmTUS28hvKgF1n+L+i8SevGWg4uoiL2yJUE2p0+EyhbQ==", 1371 + "peerDependencies": { 1372 + "svelte": "^5.0.0" 1373 + } 1374 + }, 1375 + "node_modules/@ethercorps/sveltekit-og": { 1376 + "version": "4.0.0-next.1", 1377 + "resolved": "https://registry.npmjs.org/@ethercorps/sveltekit-og/-/sveltekit-og-4.0.0-next.1.tgz", 1378 + "integrity": "sha512-cZ7cbaMmepUAXBNHL1kHEo7NKfo18AMgNHJhIrnz3MH0zogL4uzhbyjnkQsdvcMaGppJb7XF6yAQFvhSNBIjkA==", 1379 + "dependencies": { 1380 + "@ethercorps/svelte-h2j": "5.0.0-next.2", 1381 + "@resvg/resvg-js": "^2.6.0", 1382 + "satori": "^0.10.11" 1383 + }, 1384 + "peerDependencies": { 1385 + "svelte": "^5.0.0" 1386 + } 1387 + }, 1366 1388 "node_modules/@floating-ui/core": { 1367 1389 "version": "1.6.8", 1368 1390 "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", ··· 2156 2178 "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", 2157 2179 "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==" 2158 2180 }, 2181 + "node_modules/@resvg/resvg-js": { 2182 + "version": "2.6.2", 2183 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", 2184 + "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==", 2185 + "engines": { 2186 + "node": ">= 10" 2187 + }, 2188 + "optionalDependencies": { 2189 + "@resvg/resvg-js-android-arm-eabi": "2.6.2", 2190 + "@resvg/resvg-js-android-arm64": "2.6.2", 2191 + "@resvg/resvg-js-darwin-arm64": "2.6.2", 2192 + "@resvg/resvg-js-darwin-x64": "2.6.2", 2193 + "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", 2194 + "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", 2195 + "@resvg/resvg-js-linux-arm64-musl": "2.6.2", 2196 + "@resvg/resvg-js-linux-x64-gnu": "2.6.2", 2197 + "@resvg/resvg-js-linux-x64-musl": "2.6.2", 2198 + "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", 2199 + "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", 2200 + "@resvg/resvg-js-win32-x64-msvc": "2.6.2" 2201 + } 2202 + }, 2203 + "node_modules/@resvg/resvg-js-android-arm-eabi": { 2204 + "version": "2.6.2", 2205 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz", 2206 + "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==", 2207 + "cpu": [ 2208 + "arm" 2209 + ], 2210 + "optional": true, 2211 + "os": [ 2212 + "android" 2213 + ], 2214 + "engines": { 2215 + "node": ">= 10" 2216 + } 2217 + }, 2218 + "node_modules/@resvg/resvg-js-android-arm64": { 2219 + "version": "2.6.2", 2220 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz", 2221 + "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==", 2222 + "cpu": [ 2223 + "arm64" 2224 + ], 2225 + "optional": true, 2226 + "os": [ 2227 + "android" 2228 + ], 2229 + "engines": { 2230 + "node": ">= 10" 2231 + } 2232 + }, 2233 + "node_modules/@resvg/resvg-js-darwin-arm64": { 2234 + "version": "2.6.2", 2235 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.2.tgz", 2236 + "integrity": "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==", 2237 + "cpu": [ 2238 + "arm64" 2239 + ], 2240 + "optional": true, 2241 + "os": [ 2242 + "darwin" 2243 + ], 2244 + "engines": { 2245 + "node": ">= 10" 2246 + } 2247 + }, 2248 + "node_modules/@resvg/resvg-js-darwin-x64": { 2249 + "version": "2.6.2", 2250 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz", 2251 + "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==", 2252 + "cpu": [ 2253 + "x64" 2254 + ], 2255 + "optional": true, 2256 + "os": [ 2257 + "darwin" 2258 + ], 2259 + "engines": { 2260 + "node": ">= 10" 2261 + } 2262 + }, 2263 + "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": { 2264 + "version": "2.6.2", 2265 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz", 2266 + "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==", 2267 + "cpu": [ 2268 + "arm" 2269 + ], 2270 + "optional": true, 2271 + "os": [ 2272 + "linux" 2273 + ], 2274 + "engines": { 2275 + "node": ">= 10" 2276 + } 2277 + }, 2278 + "node_modules/@resvg/resvg-js-linux-arm64-gnu": { 2279 + "version": "2.6.2", 2280 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz", 2281 + "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==", 2282 + "cpu": [ 2283 + "arm64" 2284 + ], 2285 + "optional": true, 2286 + "os": [ 2287 + "linux" 2288 + ], 2289 + "engines": { 2290 + "node": ">= 10" 2291 + } 2292 + }, 2293 + "node_modules/@resvg/resvg-js-linux-arm64-musl": { 2294 + "version": "2.6.2", 2295 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz", 2296 + "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==", 2297 + "cpu": [ 2298 + "arm64" 2299 + ], 2300 + "optional": true, 2301 + "os": [ 2302 + "linux" 2303 + ], 2304 + "engines": { 2305 + "node": ">= 10" 2306 + } 2307 + }, 2308 + "node_modules/@resvg/resvg-js-linux-x64-gnu": { 2309 + "version": "2.6.2", 2310 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz", 2311 + "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==", 2312 + "cpu": [ 2313 + "x64" 2314 + ], 2315 + "optional": true, 2316 + "os": [ 2317 + "linux" 2318 + ], 2319 + "engines": { 2320 + "node": ">= 10" 2321 + } 2322 + }, 2323 + "node_modules/@resvg/resvg-js-linux-x64-musl": { 2324 + "version": "2.6.2", 2325 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz", 2326 + "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==", 2327 + "cpu": [ 2328 + "x64" 2329 + ], 2330 + "optional": true, 2331 + "os": [ 2332 + "linux" 2333 + ], 2334 + "engines": { 2335 + "node": ">= 10" 2336 + } 2337 + }, 2338 + "node_modules/@resvg/resvg-js-win32-arm64-msvc": { 2339 + "version": "2.6.2", 2340 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz", 2341 + "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==", 2342 + "cpu": [ 2343 + "arm64" 2344 + ], 2345 + "optional": true, 2346 + "os": [ 2347 + "win32" 2348 + ], 2349 + "engines": { 2350 + "node": ">= 10" 2351 + } 2352 + }, 2353 + "node_modules/@resvg/resvg-js-win32-ia32-msvc": { 2354 + "version": "2.6.2", 2355 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz", 2356 + "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==", 2357 + "cpu": [ 2358 + "ia32" 2359 + ], 2360 + "optional": true, 2361 + "os": [ 2362 + "win32" 2363 + ], 2364 + "engines": { 2365 + "node": ">= 10" 2366 + } 2367 + }, 2368 + "node_modules/@resvg/resvg-js-win32-x64-msvc": { 2369 + "version": "2.6.2", 2370 + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz", 2371 + "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==", 2372 + "cpu": [ 2373 + "x64" 2374 + ], 2375 + "optional": true, 2376 + "os": [ 2377 + "win32" 2378 + ], 2379 + "engines": { 2380 + "node": ">= 10" 2381 + } 2382 + }, 2159 2383 "node_modules/@rollup/plugin-commonjs": { 2160 2384 "version": "28.0.2", 2161 2385 "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.2.tgz", ··· 2479 2703 "os": [ 2480 2704 "win32" 2481 2705 ] 2706 + }, 2707 + "node_modules/@shuding/opentype.js": { 2708 + "version": "1.4.0-beta.0", 2709 + "resolved": "https://registry.npmjs.org/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz", 2710 + "integrity": "sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==", 2711 + "dependencies": { 2712 + "fflate": "^0.7.3", 2713 + "string.prototype.codepointat": "^0.2.1" 2714 + }, 2715 + "bin": { 2716 + "ot": "bin/ot" 2717 + }, 2718 + "engines": { 2719 + "node": ">= 8.0.0" 2720 + } 2482 2721 }, 2483 2722 "node_modules/@sveltejs/adapter-auto": { 2484 2723 "version": "3.3.1", ··· 3420 3659 "node": ">= 6" 3421 3660 } 3422 3661 }, 3662 + "node_modules/camelize": { 3663 + "version": "1.0.1", 3664 + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", 3665 + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", 3666 + "funding": { 3667 + "url": "https://github.com/sponsors/ljharb" 3668 + } 3669 + }, 3423 3670 "node_modules/caniuse-lite": { 3424 3671 "version": "1.0.30001688", 3425 3672 "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz", ··· 3696 3943 }, 3697 3944 "engines": { 3698 3945 "node": ">= 8" 3946 + } 3947 + }, 3948 + "node_modules/css-background-parser": { 3949 + "version": "0.1.0", 3950 + "resolved": "https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz", 3951 + "integrity": "sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==" 3952 + }, 3953 + "node_modules/css-box-shadow": { 3954 + "version": "1.0.0-3", 3955 + "resolved": "https://registry.npmjs.org/css-box-shadow/-/css-box-shadow-1.0.0-3.tgz", 3956 + "integrity": "sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==" 3957 + }, 3958 + "node_modules/css-color-keywords": { 3959 + "version": "1.0.0", 3960 + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", 3961 + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", 3962 + "engines": { 3963 + "node": ">=4" 3964 + } 3965 + }, 3966 + "node_modules/css-to-react-native": { 3967 + "version": "3.2.0", 3968 + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", 3969 + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", 3970 + "dependencies": { 3971 + "camelize": "^1.0.0", 3972 + "css-color-keywords": "^1.0.0", 3973 + "postcss-value-parser": "^4.0.2" 3699 3974 } 3700 3975 }, 3701 3976 "node_modules/cssesc": { ··· 4736 5011 } 4737 5012 }, 4738 5013 "node_modules/esrap": { 4739 - "version": "1.2.3", 4740 - "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.2.3.tgz", 4741 - "integrity": "sha512-ZlQmCCK+n7SGoqo7DnfKaP1sJZa49P01/dXzmjCASSo04p72w8EksT2NMK8CEX8DhKsfJXANioIw8VyHNsBfvQ==", 5014 + "version": "1.3.2", 5015 + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.3.2.tgz", 5016 + "integrity": "sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==", 4742 5017 "dependencies": { 4743 - "@jridgewell/sourcemap-codec": "^1.4.15", 4744 - "@types/estree": "^1.0.1" 5018 + "@jridgewell/sourcemap-codec": "^1.4.15" 4745 5019 } 4746 5020 }, 4747 5021 "node_modules/esrecurse": { ··· 4963 5237 "engines": { 4964 5238 "node": "^12.20 || >= 14.13" 4965 5239 } 5240 + }, 5241 + "node_modules/fflate": { 5242 + "version": "0.7.4", 5243 + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", 5244 + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==" 4966 5245 }, 4967 5246 "node_modules/file-entry-cache": { 4968 5247 "version": "8.0.0", ··· 5335 5614 }, 5336 5615 "engines": { 5337 5616 "node": ">= 0.4" 5617 + } 5618 + }, 5619 + "node_modules/hex-rgb": { 5620 + "version": "4.3.0", 5621 + "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.3.0.tgz", 5622 + "integrity": "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==", 5623 + "engines": { 5624 + "node": ">=6" 5625 + }, 5626 + "funding": { 5627 + "url": "https://github.com/sponsors/sindresorhus" 5338 5628 } 5339 5629 }, 5340 5630 "node_modules/http-errors": { ··· 5716 6006 "dev": true, 5717 6007 "engines": { 5718 6008 "node": ">=10" 6009 + } 6010 + }, 6011 + "node_modules/linebreak": { 6012 + "version": "1.1.0", 6013 + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", 6014 + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", 6015 + "dependencies": { 6016 + "base64-js": "0.0.8", 6017 + "unicode-trie": "^2.0.0" 6018 + } 6019 + }, 6020 + "node_modules/linebreak/node_modules/base64-js": { 6021 + "version": "0.0.8", 6022 + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", 6023 + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", 6024 + "engines": { 6025 + "node": ">= 0.4" 5719 6026 } 5720 6027 }, 5721 6028 "node_modules/lines-and-columns": { ··· 6200 6507 "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", 6201 6508 "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" 6202 6509 }, 6510 + "node_modules/pako": { 6511 + "version": "0.2.9", 6512 + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", 6513 + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" 6514 + }, 6203 6515 "node_modules/parent-module": { 6204 6516 "version": "1.0.1", 6205 6517 "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", ··· 6210 6522 }, 6211 6523 "engines": { 6212 6524 "node": ">=6" 6525 + } 6526 + }, 6527 + "node_modules/parse-css-color": { 6528 + "version": "0.2.1", 6529 + "resolved": "https://registry.npmjs.org/parse-css-color/-/parse-css-color-0.2.1.tgz", 6530 + "integrity": "sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==", 6531 + "dependencies": { 6532 + "color-name": "^1.1.4", 6533 + "hex-rgb": "^4.1.0" 6213 6534 } 6214 6535 }, 6215 6536 "node_modules/parseurl": { ··· 6544 6865 "node_modules/postcss-value-parser": { 6545 6866 "version": "4.2.0", 6546 6867 "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 6547 - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 6548 - "dev": true 6868 + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" 6549 6869 }, 6550 6870 "node_modules/prelude-ls": { 6551 6871 "version": "1.2.1", ··· 7038 7358 "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 7039 7359 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 7040 7360 }, 7361 + "node_modules/satori": { 7362 + "version": "0.10.14", 7363 + "resolved": "https://registry.npmjs.org/satori/-/satori-0.10.14.tgz", 7364 + "integrity": "sha512-abovcqmwl97WKioxpkfuMeZmndB1TuDFY/R+FymrZyiGP+pMYomvgSzVPnbNMWHHESOPosVHGL352oFbdAnJcA==", 7365 + "dependencies": { 7366 + "@shuding/opentype.js": "1.4.0-beta.0", 7367 + "css-background-parser": "^0.1.0", 7368 + "css-box-shadow": "1.0.0-3", 7369 + "css-to-react-native": "^3.0.0", 7370 + "emoji-regex": "^10.2.1", 7371 + "escape-html": "^1.0.3", 7372 + "linebreak": "^1.1.0", 7373 + "parse-css-color": "^0.2.1", 7374 + "postcss-value-parser": "^4.2.0", 7375 + "yoga-wasm-web": "^0.3.3" 7376 + }, 7377 + "engines": { 7378 + "node": ">=16" 7379 + } 7380 + }, 7381 + "node_modules/satori/node_modules/emoji-regex": { 7382 + "version": "10.4.0", 7383 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", 7384 + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" 7385 + }, 7041 7386 "node_modules/semver": { 7042 7387 "version": "7.6.3", 7043 7388 "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", ··· 7369 7714 "node": ">=8" 7370 7715 } 7371 7716 }, 7717 + "node_modules/string.prototype.codepointat": { 7718 + "version": "0.2.1", 7719 + "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", 7720 + "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" 7721 + }, 7372 7722 "node_modules/strip-ansi": { 7373 7723 "version": "7.1.0", 7374 7724 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", ··· 7504 7854 } 7505 7855 }, 7506 7856 "node_modules/svelte": { 7507 - "version": "5.12.0", 7508 - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.12.0.tgz", 7509 - "integrity": "sha512-nOd7uj0D/4A3IrHnltaFYndVPGViYSs0s+Zi3N4uQg3owJt9RoiUdwxYx8qjorj5CtaGsx8dNYsFVbH6czrGNg==", 7857 + "version": "5.15.0", 7858 + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.15.0.tgz", 7859 + "integrity": "sha512-YWl8rAd4hSjERLtLvP6h2pflGtmrJwv+L12BgrOtHYJCpvLS9WKp/YNAdyolw3FymXtcYZqhSWvWlu5O1X7tgQ==", 7510 7860 "dependencies": { 7511 7861 "@ampproject/remapping": "^2.3.0", 7512 7862 "@jridgewell/sourcemap-codec": "^1.5.0", ··· 7516 7866 "aria-query": "^5.3.1", 7517 7867 "axobject-query": "^4.1.0", 7518 7868 "esm-env": "^1.2.1", 7519 - "esrap": "^1.2.3", 7869 + "esrap": "^1.3.2", 7520 7870 "is-reference": "^3.0.3", 7521 7871 "locate-character": "^3.0.0", 7522 7872 "magic-string": "^0.30.11", ··· 7867 8217 "globrex": "^0.1.2" 7868 8218 } 7869 8219 }, 8220 + "node_modules/tiny-inflate": { 8221 + "version": "1.0.3", 8222 + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", 8223 + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" 8224 + }, 7870 8225 "node_modules/tlds": { 7871 8226 "version": "1.255.0", 7872 8227 "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.255.0.tgz", ··· 8019 8374 "version": "6.20.0", 8020 8375 "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", 8021 8376 "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" 8377 + }, 8378 + "node_modules/unicode-trie": { 8379 + "version": "2.0.0", 8380 + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", 8381 + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", 8382 + "dependencies": { 8383 + "pako": "^0.2.5", 8384 + "tiny-inflate": "^1.0.0" 8385 + } 8022 8386 }, 8023 8387 "node_modules/unpipe": { 8024 8388 "version": "1.0.0", ··· 8808 9172 "funding": { 8809 9173 "url": "https://github.com/sponsors/sindresorhus" 8810 9174 } 9175 + }, 9176 + "node_modules/yoga-wasm-web": { 9177 + "version": "0.3.3", 9178 + "resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz", 9179 + "integrity": "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==" 8811 9180 }, 8812 9181 "node_modules/zimmerframe": { 8813 9182 "version": "1.1.2",
+1
package.json
··· 47 47 "@atproto/lex-cli": "^0.5.3", 48 48 "@atproto/oauth-client-node": "^0.2.4", 49 49 "@atproto/xrpc-server": "^0.7.4", 50 + "@ethercorps/sveltekit-og": "^4.0.0-next.1", 50 51 "@libsql/client": "^0.9.0", 51 52 "@node-rs/argon2": "^1.1.0", 52 53 "@oslojs/crypto": "^1.0.1",
+15
src/app.html
··· 15 15 data-website-id="a372b43f-99c8-4f8e-a1bf-0ea1f726df8a" 16 16 data-domains="skywatched.app" 17 17 ></script> 18 + 19 + <meta name="description" content="review movies and shows with your bluesky friends." /> 20 + 21 + <meta property="og:url" content="https://skywatched.app" /> 22 + <meta property="og:type" content="website" /> 23 + <meta property="og:title" content="skywatched" /> 24 + <meta property="og:description" content="review movies and shows with your bluesky friends." /> 25 + <meta property="og:image" content="/og.jpg" /> 26 + 27 + <meta name="twitter:card" content="summary_large_image" /> 28 + <meta property="twitter:domain" content="skywatched.app" /> 29 + <meta property="twitter:url" content="https://skywatched.app" /> 30 + <meta name="twitter:title" content="skywatched" /> 31 + <meta name="twitter:description" content="review movies and shows with your bluesky friends." /> 32 + <meta name="twitter:image" content="/og.jpg" /> 18 33 </head> 19 34 <body data-sveltekit-preload-data="hover" class="h-full bg-black text-white"> 20 35 <div style="display: contents">%sveltekit.body%</div>
static/og.jpg

This is a binary file and will not be displayed.

+1 -1
src/lib/db.ts
··· 57 57 } 58 58 59 59 export async function getRecentRecords(): Promise<MainRecord[]> { 60 - const response = await fetch(`${env.BACKEND_URL}/api/most-recent-records?limit=10`); 60 + const response = await fetch(`${env.BACKEND_URL}/api/most-recent-records?limit=50`); 61 61 const data = await response.json(); 62 62 return data; 63 63 }
+10
src/lib/utils.ts
··· 4 4 export function cn(...inputs: ClassValue[]) { 5 5 return twMerge(clsx(inputs)); 6 6 } 7 + 8 + export function nameToId(name: string) { 9 + // replace spaces with hyphens, non-alphanumeric characters with empty string, replace multiple hyphens with a single hyphen, max length 100 10 + return name 11 + .toLowerCase() 12 + .replace(/ /g, '-') 13 + .replace(/[^a-z0-9-]/g, '') 14 + .replace(/-+/g, '-') 15 + .slice(0, 100); 16 + }
+16
src/routes/+page.server.ts
··· 2 2 import { isValidHandle } from '@atproto/syntax'; 3 3 import { error, redirect, type Actions } from '@sveltejs/kit'; 4 4 5 + import { getRecentRecords, type MainRecord } from '$lib/db'; 6 + 7 + let feed: MainRecord[] = []; 8 + let lastUpdated: Date | undefined = undefined; 9 + 10 + /** @type {import('./$types').PageServerLoad} */ 11 + export async function load() { 12 + const now = new Date(); 13 + // if lastUpdated is more than 2 minutes ago, update the feed 14 + if (!lastUpdated || now.getTime() - lastUpdated.getTime() > 2 * 60 * 1000) { 15 + feed = await getRecentRecords(); 16 + lastUpdated = now; 17 + } 18 + return { feed }; 19 + } 20 + 5 21 export const actions: Actions = { 6 22 login: async ({ request }) => { 7 23 const formData = await request.formData();
+16 -31
src/routes/+page.svelte
··· 1 1 <script> 2 + import Container from '$lib/Components/Container.svelte'; 3 + import ReviewList from '$lib/Components/ReviewList.svelte'; 2 4 import { rateMovieModal, showLoginModal } from '$lib/state.svelte'; 3 5 4 6 const { data } = $props(); ··· 8 10 <title>skywatched</title> 9 11 </svelte:head> 10 12 11 - <div class="h-screen bg-base-900"> 12 - <div class="relative isolate flex h-screen items-center justify-center overflow-hidden"> 13 - <img src="/movie.webp" alt="" class="absolute inset-0 -z-10 size-full object-cover" /> 14 - 15 - <div class="absolute inset-0 -z-10 bg-base-950/70 backdrop-blur-sm"></div> 16 - 17 - <div 18 - class="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80" 19 - aria-hidden="true" 20 - > 21 - <div 22 - class="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-accent-500 to-accent-400 opacity-20 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]" 23 - style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)" 24 - ></div> 13 + <Container class="max-w-8xl relative z-10 w-full md:max-w-3xl lg:max-w-5xl xl:max-w-6xl"> 14 + <div class="max-w-8xl mx-auto flex w-full flex-col md:flex-row"> 15 + <div class="flex grow flex-col gap-4 py-8 md:border-r border-base-900"> 16 + <h1 class="text-balance px-8 text-3xl pb-6 font-semibold tracking-tight text-base-100"> 17 + Recent reviews 18 + </h1> 19 + {#if data.feed.length > 0} 20 + <ReviewList reviews={data.feed}/> 21 + {/if} 25 22 </div> 26 23 27 - <div class="text-center"> 28 - <h1 class="text-balance text-5xl font-semibold tracking-tight text-base-100 sm:text-7xl"> 29 - skywatched 30 - </h1> 31 - <p class="text-md mt-8 text-pretty font-medium text-base-300 sm:text-lg"> 32 - review movies and shows with your bluesky account. 24 + <div class="order-first md:order-last px-8 text-base-100 h-96 md:h-screen flex flex-col items-center justify-center md:max-w-84"> 25 + <h1 class="tracking-tight font-semibold text-balance text-3xl sm:text-5xl">skywatched</h1> 26 + <p class="text-md mt-8 text-pretty font-medium text-center text-base-300 sm:text-lg max-w-xs"> 27 + review movies and shows with your bluesky friends. 33 28 </p> 34 29 {#if !data.user} 35 30 <button ··· 53 48 </button> 54 49 {/if} 55 50 </div> 56 - 57 - <div 58 - class="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]" 59 - aria-hidden="true" 60 - > 61 - <div 62 - class="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-accent-500 to-accent-400 opacity-20 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]" 63 - style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)" 64 - ></div> 65 - </div> 66 51 </div> 67 - </div> 52 + </Container>
static/fonts/inter-latin-ext-400-normal.woff

This is a binary file and will not be displayed.

+4 -2
src/lib/Components/ItemCard.svelte
··· 1 1 <script lang="ts"> 2 2 import { rateMovieModal, watchedItems } from '$lib/state.svelte'; 3 - import { cn } from '$lib/utils'; 3 + import { cn, nameToId } from '$lib/utils'; 4 4 import Rating from './Rating.svelte'; 5 5 6 6 const { ··· 85 85 </div> 86 86 <div class="mt-2 flex justify-between"> 87 87 <h3 class="sm:text-md text-sm font-medium text-base-50"> 88 - <a href="/{item.movieId ? 'movie' : 'tv'}/{item.movieId ?? item.showId}"> 88 + <a 89 + href="/{item.movieId ? 'movie' : 'tv'}/{item.movieId ?? item.showId}-{nameToId(item.title ?? item.name ?? '')}" 90 + > 89 91 <span aria-hidden="true" class="absolute inset-0"></span> 90 92 <div class="line-clamp-2 max-w-full"> 91 93 {item.title ?? item.name}
+5 -4
src/lib/Components/ReviewCard.svelte
··· 3 3 import { toast } from 'svelte-sonner'; 4 4 import Rating from './Rating.svelte'; 5 5 import RelativeTime from './relative-time/RelativeTime.svelte'; 6 + import { nameToId } from '$lib/utils'; 6 7 7 8 let { data, showMovieDetails = true }: { data: MainRecord; showMovieDetails?: boolean } = 8 9 $props(); ··· 15 16 {#if showMovieDetails} 16 17 <a 17 18 href={data.record.item.ref === 'tmdb:m' 18 - ? `/movie/${data.record.item.value}` 19 - : `/tv/${data.record.item.value}`} 19 + ? `/movie/${data.record.item.value}-${nameToId(data.record.metadata?.title ?? '')}` 20 + : `/tv/${data.record.item.value}-${nameToId(data.record.metadata?.title ?? '')}`} 20 21 class="relative z-20 mr-4 aspect-[2/3] h-32 w-auto shrink-0 overflow-hidden rounded-md border border-base-800 bg-base-900/50 transition-opacity duration-75 hover:opacity-80 group-hover:opacity-75" 21 22 > 22 23 {#if data.record.metadata?.poster_path} ··· 53 54 {#if showMovieDetails} 54 55 <a 55 56 href={data.record.item.ref === 'tmdb:m' 56 - ? `/movie/${data.record.item.value}` 57 - : `/tv/${data.record.item.value}`} 57 + ? `/movie/${data.record.item.value}-${nameToId(data.record.metadata?.title ?? '')}` 58 + : `/tv/${data.record.item.value}-${nameToId(data.record.metadata?.title ?? '')}`} 58 59 class="title text-2xl font-bold hover:text-accent-200" 59 60 style:--name={`title-${data.record.item.value}`} 60 61 >
+1 -1
src/routes/[kind]/[id]/+page.server.ts
··· 10 10 11 11 /** @type {import('./$types').PageServerLoad} */ 12 12 export async function load(event) { 13 - const id = parseInt(event.params.id); 13 + const id = parseInt(event.params.id.split('-')[0]); 14 14 const kind = event.params.kind; 15 15 16 16 if (kind !== 'movie' && kind !== 'tv') {
+19 -2
src/routes/[kind]/[id]/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import { type PageData } from './$types'; 3 - import { cn } from '$lib/utils'; 3 + import { cn, nameToId } from '$lib/utils'; 4 4 import { rateMovieModal, videoPlayer, watchedItems } from '$lib/state.svelte'; 5 + 5 6 6 7 import Container from '$lib/Components/Container.svelte'; 7 8 import ItemsList from '$lib/Components/ItemsList.svelte'; 8 9 import ReviewList from '$lib/Components/ReviewList.svelte'; 9 10 import Avatar from '$lib/Components/Avatar.svelte'; 11 + import { page } from '$app/stores'; 10 12 11 13 let { data }: { data: PageData } = $props(); 12 14 </script> 13 15 14 16 <svelte:head> 15 17 <title>{data.result.title ?? data.result.name ?? ''} | skywatched</title> 18 + 19 + <meta name="description" content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" /> 20 + 21 + <meta property="og:url" content="{$page.url.href}" /> 22 + <meta property="og:type" content="website" /> 23 + <meta property="og:title" content="{data.result.title ?? data.result.name ?? ''} | skywatched" /> 24 + <meta property="og:description" content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" /> 25 + <meta property="og:image" content="{$page.url.href}/og.png" /> 26 + 27 + <meta name="twitter:card" content="summary_large_image" /> 28 + <meta property="twitter:domain" content="skywatched.app" /> 29 + <meta property="twitter:url" content="{$page.url.href}" /> 30 + <meta name="twitter:title" content="{data.result.title ?? data.result.name ?? ''} | skywatched" /> 31 + <meta name="twitter:description" content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" /> 32 + <meta name="twitter:image" content="{$page.url.href}/og.png" /> 16 33 </svelte:head> 17 34 18 35 {#snippet buttons()} ··· 149 166 150 167 <div class={cn('flex gap-x-6 overflow-x-auto')}> 151 168 {#each data.cast as castMember} 152 - <a href={`/cast/${castMember.id}`} class="flex flex-col items-center gap-1"> 169 + <a href={`/cast/${castMember.id}-${nameToId(castMember.name)}`} class="flex flex-col items-center gap-1"> 153 170 <Avatar 154 171 src={castMember.profile_path 155 172 ? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path
+3 -1
src/routes/api/rate/+server.ts
··· 33 33 createdAt: string; 34 34 updatedAt: string; 35 35 }; 36 + from?: string; 36 37 }; 37 38 } = { 38 39 repo: did, ··· 43 44 ref: `tmdb:${kind === 'movie' ? 'm' : 's'}`, 44 45 value: id.toString() 45 46 }, 46 - rating: { value: rating * 2, createdAt: new Date().toISOString() } 47 + rating: { value: rating * 2, createdAt: new Date().toISOString() }, 48 + from: 'skywatched' 47 49 } 48 50 }; 49 51 if (review) {
+1 -1
src/routes/cast/[id]/+page.server.ts
··· 3 3 4 4 /** @type {import('./$types').PageServerLoad} */ 5 5 export async function load(event) { 6 - const id = parseInt(event.params.id); 6 + const id = parseInt(event.params.id.split('-')[0]); 7 7 8 8 if (!id) { 9 9 return error(404, 'Not found');
+72
src/routes/[kind]/[id]/og.png/+server.ts
··· 1 + // src/routes/og/+server.ts 2 + import { getDetails } from '$lib/server/movies'; 3 + import { ImageResponse } from '@ethercorps/sveltekit-og'; 4 + import { error, type RequestHandler } from '@sveltejs/kit'; 5 + 6 + const template = (data: { 7 + backdrop_path: string; 8 + poster_path: string; 9 + title?: string; 10 + name?: string; 11 + }) => { 12 + return ` 13 + <div tw="bg-zinc-900 flex flex-col w-full h-full items-center justify-center"> 14 + 15 + <div tw="flex absolute bottom-0 left-0 right-0 top-0 bg-sky-400"> 16 + <img src="https://image.tmdb.org/t/p/w780${data.backdrop_path}" alt="" class="flex h-full w-full rounded-xl opacity-50" /> 17 + 18 + <div tw="flex absolute h-full w-full bg-black/80"> 19 + </div> 20 + </div> 21 + 22 + <div tw="flex flex-row w-full py-8 px-16 items-center justify-start"> 23 + <div tw="flex h-auto aspect-[3/2] w-72"> 24 + <img src="https://image.tmdb.org/t/p/w500${data.poster_path}" alt="" 25 + class="flex h-full w-full rounded-xl border-2 border-zinc-800" style="border-radius: 16px; border-width: 1px; border-color: #3f3f46;" /> 26 + </div> 27 + 28 + <h2 tw="flex flex-col text-7xl font-bold text-zinc-100 text-left px-12 max-w-3xl"> 29 + <span tw="flex tracking-tight">${data.title ?? data.name}</span> 30 + </h2> 31 + 32 + 33 + </div> 34 + 35 + <div tw="flex text-5xl py-8 text-sky-400"> 36 + rate on skywatched.app 37 + </div> 38 + </div>`; 39 + }; 40 + 41 + const host = import.meta.env.DEV ? 'http://localhost:5173' : 'https://skywatched.app'; 42 + const fontPath = `fonts/inter-latin-ext-400-normal.woff`; 43 + const fontFile = await fetch(`${host}/${fontPath}`); 44 + const fontData: ArrayBuffer = await fontFile.arrayBuffer(); 45 + 46 + export const GET: RequestHandler = async ({ params }) => { 47 + const id = parseInt(params.id?.split('-')[0] ?? ''); 48 + const kind = params.kind; 49 + 50 + if (kind !== 'movie' && kind !== 'tv') { 51 + return error(404, 'Not found'); 52 + } 53 + 54 + if (!id) { 55 + return error(404, 'Not found'); 56 + } 57 + const result = await getDetails(id, kind); 58 + 59 + return new ImageResponse( 60 + template(result), 61 + { 62 + fonts: [ 63 + { 64 + name: 'Inter Latin', 65 + data: fontData, 66 + weight: 400 67 + } 68 + ] 69 + }, 70 + {} 71 + ); 72 + };
+56
src/routes/review/[uri]/og/+server.ts
··· 1 + // src/routes/og/+server.ts 2 + import { getRecordByUri, type MainRecord } from '$lib/db'; 3 + import { ImageResponse } from '@ethercorps/sveltekit-og'; 4 + import { error, type RequestHandler } from '@sveltejs/kit'; 5 + 6 + const template = (data: MainRecord) => { 7 + return ` 8 + <div tw="bg-zinc-900 flex w-full h-full items-center justify-center"> 9 + 10 + <div tw="flex absolute bottom-0 left-0 right-0 top-0 bg-sky-400"> 11 + <img src="https://image.tmdb.org/t/p/w780${data.record.metadata?.backdrop_path}" alt="" class="flex h-full w-full rounded-xl opacity-50" /> 12 + 13 + <div tw="flex absolute h-full w-full bg-black/80"> 14 + </div> 15 + </div> 16 + <div tw="flex flex-row w-full py-12 px-4 items-center justify-start p-8"> 17 + <div tw="flex h-auto aspect-[3/2] w-82"> 18 + <img src="https://image.tmdb.org/t/p/w500${data.record.metadata?.poster_path}" alt="" 19 + class="flex h-full w-full rounded-xl border-2 border-zinc-800" style="border-radius: 16px; border-width: 1px; border-color: #3f3f46;" /> 20 + </div> 21 + 22 + <h2 tw="flex flex-col text-7xl font-bold text-zinc-100 text-left px-8 max-w-3xl"> 23 + <span tw="flex tracking-tight">${data.record.metadata?.title}</span> 24 + </h2> 25 + </div> 26 + </div>`; 27 + }; 28 + 29 + const host = import.meta.env.DEV ? 'http://localhost:5173' : 'https://skywatched.app'; 30 + const fontPath = `fonts/inter-latin-ext-400-normal.woff`; 31 + const fontFile = await fetch(`${host}/${fontPath}`); 32 + const fontData: ArrayBuffer = await fontFile.arrayBuffer(); 33 + 34 + export const GET: RequestHandler = async ({ params }) => { 35 + const uri = params.uri; 36 + 37 + if (!uri) { 38 + return error(404, 'Not found'); 39 + } 40 + 41 + const record = await getRecordByUri({ uri }); 42 + 43 + return new ImageResponse( 44 + template(record), 45 + { 46 + fonts: [ 47 + { 48 + name: 'Inter Latin', 49 + data: fontData, 50 + weight: 400 51 + } 52 + ] 53 + }, 54 + {} 55 + ); 56 + };