init: deno fresh

This commit is contained in:
alikia2x (寒寒) 2025-02-06 22:16:42 +08:00
parent e7a536e613
commit cf4ff398b8
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
26 changed files with 579 additions and 805 deletions

16
.gitignore vendored
View File

@ -1,5 +1,3 @@
node_modules/
.node_modules/
built/* built/*
tests/cases/rwc/* tests/cases/rwc/*
tests/cases/perf/* tests/cases/perf/*
@ -75,4 +73,16 @@ data/filter/eval*
data/filter/train* data/filter/train*
filter/checkpoints filter/checkpoints
data/filter/model_predicted* data/filter/model_predicted*
scripts/*.ipynb scripts
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# Fresh build directory
_fresh/
# npm dependencies
node_modules/

6
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"recommendations": [
"denoland.vscode-deno",
"bradlc.vscode-tailwindcss"
]
}

12
components/Button.tsx Normal file
View File

@ -0,0 +1,12 @@
import { JSX } from "preact";
import { IS_BROWSER } from "$fresh/runtime.ts";
export function Button(props: JSX.HTMLAttributes<HTMLButtonElement>) {
return (
<button
{...props}
disabled={!IS_BROWSER || props.disabled}
class="px-2 py-1 border-gray-500 border-2 rounded bg-white hover:bg-gray-200 transition-colors"
/>
);
}

View File

@ -1,12 +1,46 @@
{ {
"tasks": { "lock": false,
"crawl-raw-bili": "deno --allow-env --allow-ffi --allow-read --allow-net --allow-write --allow-run src/db/raw/insertAidsToDB.ts", "tasks": {
"crawl-bili-aids": "deno --allow-env --allow-ffi --allow-read --allow-net --allow-write --allow-run src/db/raw/fetchAids.ts" "crawl-raw-bili": "deno --allow-env --allow-ffi --allow-read --allow-net --allow-write --allow-run src/db/raw/insertAidsToDB.ts",
}, "crawl-bili-aids": "deno --allow-env --allow-ffi --allow-read --allow-net --allow-write --allow-run src/db/raw/fetchAids.ts",
"imports": { "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
"@std/assert": "jsr:@std/assert@1", "cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
"@types/better-sqlite3": "npm:@types/better-sqlite3@^7.6.12", "manifest": "deno task cli manifest $(pwd)",
"axios": "npm:axios@^1.7.9", "start": "deno run -A --watch=static/,routes/ dev.ts",
"better-sqlite3": "npm:better-sqlite3@^11.7.2" "build": "deno run -A dev.ts build",
} "preview": "deno run -A main.ts",
"update": "deno run -A -r https://fresh.deno.dev/update ."
},
"lint": {
"rules": {
"tags": ["fresh", "recommended"]
}
},
"exclude": ["**/_fresh/*"],
"imports": {
"@std/assert": "jsr:@std/assert@1",
"@types/better-sqlite3": "npm:@types/better-sqlite3@^7.6.12",
"axios": "npm:axios@^1.7.9",
"better-sqlite3": "npm:better-sqlite3@^11.7.2",
"$fresh/": "https://deno.land/x/fresh@1.7.3/",
"preact": "https://esm.sh/preact@10.22.0",
"preact/": "https://esm.sh/preact@10.22.0/",
"@preact/signals": "https://esm.sh/*@preact/signals@1.2.2",
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1",
"tailwindcss": "npm:tailwindcss@3.4.1",
"tailwindcss/": "npm:/tailwindcss@3.4.1/",
"tailwindcss/plugin": "npm:/tailwindcss@3.4.1/plugin.js",
"$std/": "https://deno.land/std@0.216.0/"
},
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
},
"nodeModulesDir": "auto",
"fmt": {
"useTabs": true,
"lineWidth": 120,
"indentWidth": 4,
"semiColons": true
}
} }

502
deno.lock
View File

@ -1,502 +0,0 @@
{
"version": "4",
"specifiers": {
"jsr:@db/sqlite@0.12": "0.12.0",
"jsr:@denosaurs/plug@1": "1.0.6",
"jsr:@std/assert@0.217": "0.217.0",
"jsr:@std/assert@0.221": "0.221.0",
"jsr:@std/assert@1": "1.0.10",
"jsr:@std/encoding@0.221": "0.221.0",
"jsr:@std/fmt@0.221": "0.221.0",
"jsr:@std/fs@0.221": "0.221.0",
"jsr:@std/internal@^1.0.5": "1.0.5",
"jsr:@std/path@0.217": "0.217.0",
"jsr:@std/path@0.221": "0.221.0",
"npm:@types/better-sqlite3@*": "7.6.12",
"npm:@types/better-sqlite3@^7.6.12": "7.6.12",
"npm:@types/node@*": "22.5.4",
"npm:axios@^1.7.9": "1.7.9",
"npm:better-sqlite3@^11.7.2": "11.7.2"
},
"jsr": {
"@db/sqlite@0.12.0": {
"integrity": "dd1ef7f621ad50fc1e073a1c3609c4470bd51edc0994139c5bf9851de7a6d85f",
"dependencies": [
"jsr:@denosaurs/plug",
"jsr:@std/path@0.217"
]
},
"@denosaurs/plug@1.0.6": {
"integrity": "6cf5b9daba7799837b9ffbe89f3450510f588fafef8115ddab1ff0be9cb7c1a7",
"dependencies": [
"jsr:@std/encoding",
"jsr:@std/fmt",
"jsr:@std/fs",
"jsr:@std/path@0.221"
]
},
"@std/assert@0.217.0": {
"integrity": "c98e279362ca6982d5285c3b89517b757c1e3477ee9f14eb2fdf80a45aaa9642"
},
"@std/assert@0.221.0": {
"integrity": "a5f1aa6e7909dbea271754fd4ab3f4e687aeff4873b4cef9a320af813adb489a"
},
"@std/assert@1.0.10": {
"integrity": "59b5cbac5bd55459a19045d95cc7c2ff787b4f8527c0dd195078ff6f9481fbb3",
"dependencies": [
"jsr:@std/internal"
]
},
"@std/encoding@0.221.0": {
"integrity": "d1dd76ef0dc5d14088411e6dc1dede53bf8308c95d1537df1214c97137208e45"
},
"@std/fmt@0.221.0": {
"integrity": "379fed69bdd9731110f26b9085aeb740606b20428ce6af31ef6bd45ef8efa62a"
},
"@std/fs@0.221.0": {
"integrity": "028044450299de8ed5a716ade4e6d524399f035513b85913794f4e81f07da286",
"dependencies": [
"jsr:@std/assert@0.221",
"jsr:@std/path@0.221"
]
},
"@std/internal@1.0.5": {
"integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba"
},
"@std/path@0.217.0": {
"integrity": "1217cc25534bca9a2f672d7fe7c6f356e4027df400c0e85c0ef3e4343bc67d11",
"dependencies": [
"jsr:@std/assert@0.217"
]
},
"@std/path@0.221.0": {
"integrity": "0a36f6b17314ef653a3a1649740cc8db51b25a133ecfe838f20b79a56ebe0095",
"dependencies": [
"jsr:@std/assert@0.221"
]
}
},
"npm": {
"@types/better-sqlite3@7.6.12": {
"integrity": "sha512-fnQmj8lELIj7BSrZQAdBMHEHX8OZLYIHXqAKT1O7tDfLxaINzf00PMjw22r3N/xXh0w/sGHlO6SVaCQ2mj78lg==",
"dependencies": [
"@types/node"
]
},
"@types/node@22.5.4": {
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
"dependencies": [
"undici-types"
]
},
"asynckit@0.4.0": {
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"axios@1.7.9": {
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
"dependencies": [
"follow-redirects",
"form-data",
"proxy-from-env"
]
},
"base64-js@1.5.1": {
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"better-sqlite3@11.7.2": {
"integrity": "sha512-10a57cHVDmfNQS4jrZ9AH2t+2ekzYh5Rhbcnb4ytpmYweoLdogDmyTt5D+hLiY9b44Mx9foowb/4iXBTO2yP3Q==",
"dependencies": [
"bindings",
"prebuild-install"
]
},
"bindings@1.5.0": {
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dependencies": [
"file-uri-to-path"
]
},
"bl@4.1.0": {
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": [
"buffer",
"inherits",
"readable-stream"
]
},
"buffer@5.7.1": {
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dependencies": [
"base64-js",
"ieee754"
]
},
"chownr@1.1.4": {
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"combined-stream@1.0.8": {
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": [
"delayed-stream"
]
},
"decompress-response@6.0.0": {
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": [
"mimic-response"
]
},
"deep-extend@0.6.0": {
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"delayed-stream@1.0.0": {
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"detect-libc@2.0.3": {
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="
},
"end-of-stream@1.4.4": {
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": [
"once"
]
},
"expand-template@2.0.3": {
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
},
"file-uri-to-path@1.0.0": {
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"follow-redirects@1.15.9": {
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
},
"form-data@4.0.1": {
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": [
"asynckit",
"combined-stream",
"mime-types"
]
},
"fs-constants@1.0.0": {
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"github-from-package@0.0.0": {
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
},
"ieee754@1.2.1": {
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"inherits@2.0.4": {
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini@1.3.8": {
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"mime-db@1.52.0": {
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types@2.1.35": {
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": [
"mime-db"
]
},
"mimic-response@3.1.0": {
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
},
"minimist@1.2.8": {
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
},
"mkdirp-classic@0.5.3": {
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"napi-build-utils@1.0.2": {
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
},
"node-abi@3.71.0": {
"integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==",
"dependencies": [
"semver"
]
},
"once@1.4.0": {
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": [
"wrappy"
]
},
"prebuild-install@7.1.2": {
"integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
"dependencies": [
"detect-libc",
"expand-template",
"github-from-package",
"minimist",
"mkdirp-classic",
"napi-build-utils",
"node-abi",
"pump",
"rc",
"simple-get",
"tar-fs",
"tunnel-agent"
]
},
"proxy-from-env@1.1.0": {
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"pump@3.0.2": {
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
"dependencies": [
"end-of-stream",
"once"
]
},
"rc@1.2.8": {
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dependencies": [
"deep-extend",
"ini",
"minimist",
"strip-json-comments"
]
},
"readable-stream@3.6.2": {
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": [
"inherits",
"string_decoder",
"util-deprecate"
]
},
"safe-buffer@5.2.1": {
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"semver@7.6.3": {
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
},
"simple-concat@1.0.1": {
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get@4.0.1": {
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"dependencies": [
"decompress-response",
"once",
"simple-concat"
]
},
"string_decoder@1.3.0": {
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": [
"safe-buffer"
]
},
"strip-json-comments@2.0.1": {
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
},
"tar-fs@2.1.1": {
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"dependencies": [
"chownr",
"mkdirp-classic",
"pump",
"tar-stream"
]
},
"tar-stream@2.2.0": {
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": [
"bl",
"end-of-stream",
"fs-constants",
"inherits",
"readable-stream"
]
},
"tunnel-agent@0.6.0": {
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dependencies": [
"safe-buffer"
]
},
"undici-types@6.19.8": {
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
},
"util-deprecate@1.0.2": {
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"wrappy@1.0.2": {
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
},
"redirects": {
"https://deno.land/x/postgres/mod.ts": "https://deno.land/x/postgres@v0.19.3/mod.ts",
"https://deno.land/x/sqlite/mod.ts": "https://deno.land/x/sqlite@v3.9.1/mod.ts"
},
"remote": {
"https://deno.land/std@0.113.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
"https://deno.land/std@0.113.0/_util/os.ts": "dfb186cc4e968c770ab6cc3288bd65f4871be03b93beecae57d657232ecffcac",
"https://deno.land/std@0.113.0/fs/_util.ts": "f2ce811350236ea8c28450ed822a5f42a0892316515b1cd61321dec13569c56b",
"https://deno.land/std@0.113.0/fs/copy.ts": "631bbafbfe6cba282158abc8aeb7e8251cc69a7ec28ce12878ea1b75fec2add4",
"https://deno.land/std@0.113.0/fs/empty_dir.ts": "5f08b263dd064dc7917c4bbeb13de0f5505a664b9cdfe312fa86e7518cfaeb84",
"https://deno.land/std@0.113.0/fs/ensure_dir.ts": "b7c103dc41a3d1dbbb522bf183c519c37065fdc234831a4a0f7d671b1ed5fea7",
"https://deno.land/std@0.113.0/fs/ensure_file.ts": "c06031af24368e80c330897e4b8e9109efc8602ffabc8f3e2306be07529e1d13",
"https://deno.land/std@0.113.0/fs/ensure_link.ts": "26e54363508b822afd87a3f6e873bbbcd6b5993dd638f8170758c16262a75065",
"https://deno.land/std@0.113.0/fs/ensure_symlink.ts": "c07b6d19ef58b6f5c671ffa942e7f9be50315f4f78e2f9f511626fd2e13beccc",
"https://deno.land/std@0.113.0/fs/eol.ts": "afaebaaac36f48c423b920c836551997715672b80a0fee9aa7667c181a94f2df",
"https://deno.land/std@0.113.0/fs/exists.ts": "c3c3335a212bd945bb75df379096ab57fb6c86598fa273dfb24da3b3939a951e",
"https://deno.land/std@0.113.0/fs/expand_glob.ts": "7c9173f93044051456b829a3f5a3676e58ba70b6ce4aae62cf24757b58556205",
"https://deno.land/std@0.113.0/fs/mod.ts": "26eee4b52a8c516e37d464094b080ff6822883e7f01ff0ba0a72b8dcd54b9927",
"https://deno.land/std@0.113.0/fs/move.ts": "4623058e39bbbeb3ad30aeff9c974c55d2d574ad7c480295c12b04c244686a99",
"https://deno.land/std@0.113.0/fs/walk.ts": "f633829f967d2979ab285dbfb09eb0d7d000fd175b95156b63fcede435d1a807",
"https://deno.land/std@0.113.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853",
"https://deno.land/std@0.113.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4",
"https://deno.land/std@0.113.0/path/_util.ts": "2e06a3b9e79beaf62687196bd4b60a4c391d862cfa007a20fc3a39f778ba073b",
"https://deno.land/std@0.113.0/path/common.ts": "f41a38a0719a1e85aa11c6ba3bea5e37c15dd009d705bd8873f94c833568cbc4",
"https://deno.land/std@0.113.0/path/glob.ts": "ea87985765b977cc284b92771003b2070c440e0807c90e1eb0ff3e095911a820",
"https://deno.land/std@0.113.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12",
"https://deno.land/std@0.113.0/path/posix.ts": "34349174b9cd121625a2810837a82dd8b986bbaaad5ade690d1de75bbb4555b2",
"https://deno.land/std@0.113.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
"https://deno.land/std@0.113.0/path/win32.ts": "11549e8c6df8307a8efcfa47ad7b2a75da743eac7d4c89c9723a944661c8bd2e",
"https://deno.land/std@0.214.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5",
"https://deno.land/std@0.214.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8",
"https://deno.land/std@0.214.0/async/delay.ts": "8e1d18fe8b28ff95885e2bc54eccec1713f57f756053576d8228e6ca110793ad",
"https://deno.land/std@0.214.0/bytes/copy.ts": "f29c03168853720dfe82eaa57793d0b9e3543ebfe5306684182f0f1e3bfd422a",
"https://deno.land/std@0.214.0/crypto/_fnv/fnv32.ts": "ba2c5ef976b9f047d7ce2d33dfe18671afc75154bcf20ef89d932b2fe8820535",
"https://deno.land/std@0.214.0/crypto/_fnv/fnv64.ts": "580cadfe2ff333fe253d15df450f927c8ac7e408b704547be26aab41b5772558",
"https://deno.land/std@0.214.0/crypto/_fnv/mod.ts": "8dbb60f062a6e77b82f7a62ac11fabfba52c3cd408c21916b130d8f57a880f96",
"https://deno.land/std@0.214.0/crypto/_fnv/util.ts": "27b36ce3440d0a180af6bf1cfc2c326f68823288540a354dc1d636b781b9b75f",
"https://deno.land/std@0.214.0/crypto/_wasm/lib/deno_std_wasm_crypto.generated.mjs": "76c727912539737def4549bb62a96897f37eb334b979f49c57b8af7a1617635e",
"https://deno.land/std@0.214.0/crypto/_wasm/mod.ts": "c55f91473846827f077dfd7e5fc6e2726dee5003b6a5747610707cdc638a22ba",
"https://deno.land/std@0.214.0/crypto/crypto.ts": "4448f8461c797adba8d70a2c60f7795a546d7a0926e96366391bffdd06491c16",
"https://deno.land/std@0.214.0/datetime/_common.ts": "a62214c1924766e008e27d3d843ceba4b545dc2aa9880de0ecdef9966d5736b6",
"https://deno.land/std@0.214.0/datetime/parse.ts": "bb248bbcb3cd54bcaf504a1ee670fc4695e429d9019c06af954bbe2bcb8f1d02",
"https://deno.land/std@0.214.0/encoding/_util.ts": "beacef316c1255da9bc8e95afb1fa56ed69baef919c88dc06ae6cb7a6103d376",
"https://deno.land/std@0.214.0/encoding/base64.ts": "96e61a556d933201266fea84ae500453293f2aff130057b579baafda096a96bc",
"https://deno.land/std@0.214.0/encoding/hex.ts": "4d47d3b25103cf81a2ed38f54b394d39a77b63338e1eaa04b70c614cb45ec2e6",
"https://deno.land/std@0.214.0/fmt/colors.ts": "aeaee795471b56fc62a3cb2e174ed33e91551b535f44677f6320336aabb54fbb",
"https://deno.land/std@0.214.0/io/buf_reader.ts": "c73aad99491ee6db3d6b001fa4a780e9245c67b9296f5bad9c0fa7384e35d47a",
"https://deno.land/std@0.214.0/io/buf_writer.ts": "f82f640c8b3a820f600a8da429ad0537037c7d6a78426bbca2396fb1f75d3ef4",
"https://deno.land/std@0.214.0/path/_common/assert_path.ts": "2ca275f36ac1788b2acb60fb2b79cb06027198bc2ba6fb7e163efaedde98c297",
"https://deno.land/std@0.214.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2",
"https://deno.land/std@0.214.0/path/_common/common.ts": "6157c7ec1f4db2b4a9a187efd6ce76dcaf1e61cfd49f87e40d4ea102818df031",
"https://deno.land/std@0.214.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c",
"https://deno.land/std@0.214.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
"https://deno.land/std@0.214.0/path/_common/format.ts": "92500e91ea5de21c97f5fe91e178bae62af524b72d5fcd246d6d60ae4bcada8b",
"https://deno.land/std@0.214.0/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf",
"https://deno.land/std@0.214.0/path/_common/glob_to_reg_exp.ts": "2007aa87bed6eb2c8ae8381adcc3125027543d9ec347713c1ad2c68427330770",
"https://deno.land/std@0.214.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
"https://deno.land/std@0.214.0/path/_common/normalize_string.ts": "dfdf657a1b1a7db7999f7c575ee7e6b0551d9c20f19486c6c3f5ff428384c965",
"https://deno.land/std@0.214.0/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607",
"https://deno.land/std@0.214.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a",
"https://deno.land/std@0.214.0/path/_common/to_file_url.ts": "7f76adbc83ece1bba173e6e98a27c647712cab773d3f8cbe0398b74afc817883",
"https://deno.land/std@0.214.0/path/_interface.ts": "a1419fcf45c0ceb8acdccc94394e3e94f99e18cfd32d509aab514c8841799600",
"https://deno.land/std@0.214.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15",
"https://deno.land/std@0.214.0/path/basename.ts": "5d341aadb7ada266e2280561692c165771d071c98746fcb66da928870cd47668",
"https://deno.land/std@0.214.0/path/common.ts": "03e52e22882402c986fe97ca3b5bb4263c2aa811c515ce84584b23bac4cc2643",
"https://deno.land/std@0.214.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36",
"https://deno.land/std@0.214.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c",
"https://deno.land/std@0.214.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441",
"https://deno.land/std@0.214.0/path/format.ts": "98fad25f1af7b96a48efb5b67378fcc8ed77be895df8b9c733b86411632162af",
"https://deno.land/std@0.214.0/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069",
"https://deno.land/std@0.214.0/path/glob_to_regexp.ts": "83c5fd36a8c86f5e72df9d0f45317f9546afa2ce39acaafe079d43a865aced08",
"https://deno.land/std@0.214.0/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7",
"https://deno.land/std@0.214.0/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141",
"https://deno.land/std@0.214.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a",
"https://deno.land/std@0.214.0/path/join_globs.ts": "e9589869a33dc3982101898ee50903db918ca00ad2614dbe3934d597d7b1fbea",
"https://deno.land/std@0.214.0/path/mod.ts": "ffeaccb713dbe6c72e015b7c767f753f8ec5fbc3b621ff5eeee486ffc2c0ddda",
"https://deno.land/std@0.214.0/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352",
"https://deno.land/std@0.214.0/path/normalize_glob.ts": "98ee8268fad271193603271c203ae973280b5abfbdd2cbca1053fd2af71869ca",
"https://deno.land/std@0.214.0/path/parse.ts": "65e8e285f1a63b714e19ef24b68f56e76934c3df0b6e65fd440d3991f4f8aefb",
"https://deno.land/std@0.214.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d",
"https://deno.land/std@0.214.0/path/posix/basename.ts": "39ee27a29f1f35935d3603ccf01d53f3d6e0c5d4d0f84421e65bd1afeff42843",
"https://deno.land/std@0.214.0/path/posix/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4",
"https://deno.land/std@0.214.0/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1",
"https://deno.land/std@0.214.0/path/posix/dirname.ts": "6535d2bdd566118963537b9dda8867ba9e2a361015540dc91f5afbb65c0cce8b",
"https://deno.land/std@0.214.0/path/posix/extname.ts": "8d36ae0082063c5e1191639699e6f77d3acf501600a3d87b74943f0ae5327427",
"https://deno.land/std@0.214.0/path/posix/format.ts": "185e9ee2091a42dd39e2a3b8e4925370ee8407572cee1ae52838aed96310c5c1",
"https://deno.land/std@0.214.0/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40",
"https://deno.land/std@0.214.0/path/posix/glob_to_regexp.ts": "54d3ff40f309e3732ab6e5b19d7111d2d415248bcd35b67a99defcbc1972e697",
"https://deno.land/std@0.214.0/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede",
"https://deno.land/std@0.214.0/path/posix/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9",
"https://deno.land/std@0.214.0/path/posix/join.ts": "aef88d5fa3650f7516730865dbb951594d1a955b785e2450dbee93b8e32694f3",
"https://deno.land/std@0.214.0/path/posix/join_globs.ts": "ee2f4676c5b8a0dfa519da58b8ade4d1c4aa8dd3fe35619edec883ae9df1f8c9",
"https://deno.land/std@0.214.0/path/posix/mod.ts": "563a18c2b3ddc62f3e4a324ff0f583e819b8602a72ad880cb98c9e2e34f8db5b",
"https://deno.land/std@0.214.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91",
"https://deno.land/std@0.214.0/path/posix/normalize_glob.ts": "65f0138fa518ef9ece354f32889783fc38cdf985fb02dcf1c3b14fa47d665640",
"https://deno.land/std@0.214.0/path/posix/parse.ts": "d5bac4eb21262ab168eead7e2196cb862940c84cee572eafedd12a0d34adc8fb",
"https://deno.land/std@0.214.0/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c",
"https://deno.land/std@0.214.0/path/posix/resolve.ts": "bac20d9921beebbbb2b73706683b518b1d0c1b1da514140cee409e90d6b2913a",
"https://deno.land/std@0.214.0/path/posix/separator.ts": "c9ecae5c843170118156ac5d12dc53e9caf6a1a4c96fc8b1a0ab02dff5c847b0",
"https://deno.land/std@0.214.0/path/posix/to_file_url.ts": "7aa752ba66a35049e0e4a4be5a0a31ac6b645257d2e031142abb1854de250aaf",
"https://deno.land/std@0.214.0/path/posix/to_namespaced_path.ts": "28b216b3c76f892a4dca9734ff1cc0045d135532bfd9c435ae4858bfa5a2ebf0",
"https://deno.land/std@0.214.0/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add",
"https://deno.land/std@0.214.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d",
"https://deno.land/std@0.214.0/path/separator.ts": "c6c890507f944a1f5cb7d53b8d638d6ce3cf0f34609c8d84a10c1eaa400b77a9",
"https://deno.land/std@0.214.0/path/to_file_url.ts": "88f049b769bce411e2d2db5bd9e6fd9a185a5fbd6b9f5ad8f52bef517c4ece1b",
"https://deno.land/std@0.214.0/path/to_namespaced_path.ts": "b706a4103b104cfadc09600a5f838c2ba94dbcdb642344557122dda444526e40",
"https://deno.land/std@0.214.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808",
"https://deno.land/std@0.214.0/path/windows/basename.ts": "e2dbf31d1d6385bfab1ce38c333aa290b6d7ae9e0ecb8234a654e583cf22f8fe",
"https://deno.land/std@0.214.0/path/windows/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4",
"https://deno.land/std@0.214.0/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5",
"https://deno.land/std@0.214.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9",
"https://deno.land/std@0.214.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef",
"https://deno.land/std@0.214.0/path/windows/format.ts": "bbb5ecf379305b472b1082cd2fdc010e44a0020030414974d6029be9ad52aeb6",
"https://deno.land/std@0.214.0/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01",
"https://deno.land/std@0.214.0/path/windows/glob_to_regexp.ts": "6dcd1242bd8907aa9660cbdd7c93446e6927b201112b0cba37ca5d80f81be51b",
"https://deno.land/std@0.214.0/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a",
"https://deno.land/std@0.214.0/path/windows/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9",
"https://deno.land/std@0.214.0/path/windows/join.ts": "e0b3356615c1a75c56ebb6a7311157911659e11fd533d80d724800126b761ac3",
"https://deno.land/std@0.214.0/path/windows/join_globs.ts": "ee2f4676c5b8a0dfa519da58b8ade4d1c4aa8dd3fe35619edec883ae9df1f8c9",
"https://deno.land/std@0.214.0/path/windows/mod.ts": "7d6062927bda47c47847ffb55d8f1a37b0383840aee5c7dfc93984005819689c",
"https://deno.land/std@0.214.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780",
"https://deno.land/std@0.214.0/path/windows/normalize_glob.ts": "179c86ba89f4d3fe283d2addbe0607341f79ee9b1ae663abcfb3439db2e97810",
"https://deno.land/std@0.214.0/path/windows/parse.ts": "b9239edd892a06a06625c1b58425e199f018ce5649ace024d144495c984da734",
"https://deno.land/std@0.214.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7",
"https://deno.land/std@0.214.0/path/windows/resolve.ts": "75b2e3e1238d840782cee3d8864d82bfaa593c7af8b22f19c6422cf82f330ab3",
"https://deno.land/std@0.214.0/path/windows/separator.ts": "e51c5522140eff4f8402617c5c68a201fdfa3a1a8b28dc23587cff931b665e43",
"https://deno.land/std@0.214.0/path/windows/to_file_url.ts": "1cd63fd35ec8d1370feaa4752eccc4cc05ea5362a878be8dc7db733650995484",
"https://deno.land/std@0.214.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c",
"https://deno.land/x/postgres@v0.19.3/client.ts": "d141c65c20484c545a1119c9af7a52dcc24f75c1a5633de2b9617b0f4b2ed5c1",
"https://deno.land/x/postgres@v0.19.3/client/error.ts": "05b0e35d65caf0ba21f7f6fab28c0811da83cd8b4897995a2f411c2c83391036",
"https://deno.land/x/postgres@v0.19.3/connection/auth.ts": "db15c1659742ef4d2791b32834950278dc7a40cb931f8e434e6569298e58df51",
"https://deno.land/x/postgres@v0.19.3/connection/connection.ts": "198a0ecf92a0d2aa72db3bb88b8f412d3b1f6b87d464d5f7bff9aa3b6aff8370",
"https://deno.land/x/postgres@v0.19.3/connection/connection_params.ts": "463d7a9ed559f537a55d6928cab62e1c31b808d08cd0411b6ae461d0c0183c93",
"https://deno.land/x/postgres@v0.19.3/connection/message.ts": "20da5d80fc4d7ddb7b850083e0b3fa8734eb26642221dad89c62e27d78e57a4d",
"https://deno.land/x/postgres@v0.19.3/connection/message_code.ts": "12bcb110df6945152f9f6c63128786558d7ad1e61006920daaa16ef85b3bab7d",
"https://deno.land/x/postgres@v0.19.3/connection/packet.ts": "050aeff1fc13c9349e89451a155ffcd0b1343dc313a51f84439e3e45f64b56c8",
"https://deno.land/x/postgres@v0.19.3/connection/scram.ts": "532d4d58b565a2ab48fb5e1e14dc9bfb3bb283d535011e371e698eb4a89dd994",
"https://deno.land/x/postgres@v0.19.3/debug.ts": "8add17699191f11e6830b8c95d9de25857d221bb2cf6c4ae22254d395895c1f9",
"https://deno.land/x/postgres@v0.19.3/deps.ts": "c312038fe64b8368f8a294119f11d8f235fe67de84d7c3b0ef67b3a56628171a",
"https://deno.land/x/postgres@v0.19.3/mod.ts": "4930c7b44f8d16ea71026f7e3ef22a2322d84655edceacd55f7461a9218d8560",
"https://deno.land/x/postgres@v0.19.3/pool.ts": "2289f029e7a3bd3d460d4faa71399a920b7406c92a97c0715d6e31dbf1380ec3",
"https://deno.land/x/postgres@v0.19.3/query/array_parser.ts": "ff72d3e026e3022a1a223a6530be5663f8ebbd911ed978291314e7fe6c2f2464",
"https://deno.land/x/postgres@v0.19.3/query/decode.ts": "3e89ad2a662eab66a4f4e195ff0924d71d199af3c2f5637d1ae650301a03fa9b",
"https://deno.land/x/postgres@v0.19.3/query/decoders.ts": "6a73da1024086ab91e233648c850dccbde59248b90d87054bbbd7f0bf4a50681",
"https://deno.land/x/postgres@v0.19.3/query/encode.ts": "5b1c305bc7352a6f9fe37f235dddfc23e26419c77a133b4eaea42cf136481aa6",
"https://deno.land/x/postgres@v0.19.3/query/oid.ts": "21fc714ac212350ba7df496f88ea9e01a4ee0458911d0f2b6a81498e12e7af4c",
"https://deno.land/x/postgres@v0.19.3/query/query.ts": "510f9a27da87ed7b31b5cbcd14bf3028b441ac2ddc368483679d0b86a9d9f213",
"https://deno.land/x/postgres@v0.19.3/query/transaction.ts": "8f4eef68f8e9b4be216199404315e6e08fe1fe98afb2e640bffd077662f79678",
"https://deno.land/x/postgres@v0.19.3/utils/deferred.ts": "5420531adb6c3ea29ca8aac57b9b59bd3e4b9a938a4996bbd0947a858f611080",
"https://deno.land/x/postgres@v0.19.3/utils/utils.ts": "ca47193ea03ff5b585e487a06f106d367e509263a960b787197ce0c03113a738",
"https://deno.land/x/sqlite@v3.9.1/build/sqlite.js": "2afc7875c7b9c85d89730c4a311ab3a304e5d1bf761fbadd8c07bbdf130f5f9b",
"https://deno.land/x/sqlite@v3.9.1/build/vfs.js": "7f7778a9fe499cd10738d6e43867340b50b67d3e39142b0065acd51a84cd2e03",
"https://deno.land/x/sqlite@v3.9.1/mod.ts": "e09fc79d8065fe222578114b109b1fd60077bff1bb75448532077f784f4d6a83",
"https://deno.land/x/sqlite@v3.9.1/src/constants.ts": "90f3be047ec0a89bcb5d6fc30db121685fc82cb00b1c476124ff47a4b0472aa9",
"https://deno.land/x/sqlite@v3.9.1/src/db.ts": "03d0c860957496eadedd86e51a6e650670764630e64f56df0092e86c90752401",
"https://deno.land/x/sqlite@v3.9.1/src/error.ts": "f7a15cb00d7c3797da1aefee3cf86d23e0ae92e73f0ba3165496c3816ab9503a",
"https://deno.land/x/sqlite@v3.9.1/src/function.ts": "bc778cab7a6d771f690afa27264c524d22fcb96f1bb61959ade7922c15a4ab8d",
"https://deno.land/x/sqlite@v3.9.1/src/query.ts": "d58abda928f6582d77bad685ecf551b1be8a15e8e38403e293ec38522e030cad",
"https://deno.land/x/sqlite@v3.9.1/src/wasm.ts": "e79d0baa6e42423257fb3c7cc98091c54399254867e0f34a09b5bdef37bd9487"
},
"workspace": {
"dependencies": [
"jsr:@std/assert@1",
"npm:@types/better-sqlite3@^7.6.12",
"npm:axios@^1.7.9",
"npm:better-sqlite3@^11.7.2"
]
}
}

8
dev.ts Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env -S deno run -A --watch=static/,routes/
import dev from "$fresh/dev.ts";
import config from "./fresh.config.ts";
import "$std/dotenv/load.ts";
await dev(import.meta.url, "./main.ts", config);

View File

@ -3,7 +3,7 @@
The `type` column used in the PostgreSQL database. The `type` column used in the PostgreSQL database.
| Type | Description | | Type | Description |
|------|------------------------| | ---- | ---------------------- |
| 0 | Other | | 0 | Other |
| 1 | Original | | 1 | Original |
| 2 | Cover | | 2 | Cover |

6
fresh.config.ts Normal file
View File

@ -0,0 +1,6 @@
import { defineConfig } from "$fresh/server.ts";
import tailwind from "$fresh/plugins/tailwind.ts";
export default defineConfig({
plugins: [tailwind()],
});

27
fresh.gen.ts Normal file
View File

@ -0,0 +1,27 @@
// 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 * as $_404 from "./routes/_404.tsx";
import * as $_app from "./routes/_app.tsx";
import * as $api_joke from "./routes/api/joke.ts";
import * as $greet_name_ from "./routes/greet/[name].tsx";
import * as $index from "./routes/index.tsx";
import * as $Counter from "./islands/Counter.tsx";
import type { Manifest } from "$fresh/server.ts";
const manifest = {
routes: {
"./routes/_404.tsx": $_404,
"./routes/_app.tsx": $_app,
"./routes/api/joke.ts": $api_joke,
"./routes/greet/[name].tsx": $greet_name_,
"./routes/index.tsx": $index,
},
islands: {
"./islands/Counter.tsx": $Counter,
},
baseUrl: import.meta.url,
} satisfies Manifest;
export default manifest;

16
islands/Counter.tsx Normal file
View File

@ -0,0 +1,16 @@
import type { Signal } from "@preact/signals";
import { Button } from "../components/Button.tsx";
interface CounterProps {
count: Signal<number>;
}
export default function Counter(props: CounterProps) {
return (
<div class="flex gap-8 py-6">
<Button onClick={() => props.count.value -= 1}>-1</Button>
<p class="text-3xl tabular-nums">{props.count}</p>
<Button onClick={() => props.count.value += 1}>+1</Button>
</div>
);
}

View File

@ -1,3 +1,3 @@
1. prepare `1.mp3`, `1.txt`, `1.group` in `./data` 1. prepare `1.mp3`, `1.txt`, `1.group` in `./data`
2. `whisperAlignment/alignWithGroup` 2. `whisperAlignment/alignWithGroup`
3. `mmsAlignment 3. `mmsAlignment

13
main.ts Normal file
View File

@ -0,0 +1,13 @@
/// <reference no-default-lib="true" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
/// <reference lib="deno.ns" />
import "$std/dotenv/load.ts";
import { start } from "$fresh/server.ts";
import manifest from "./fresh.gen.ts";
import config from "./fresh.config.ts";
await start(manifest, config);

27
routes/_404.tsx Normal file
View File

@ -0,0 +1,27 @@
import { Head } from "$fresh/runtime.ts";
export default function Error404() {
return (
<>
<Head>
<title>404 - Page not found</title>
</Head>
<div class="px-4 py-8 mx-auto bg-[#86efac]">
<div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
<img
class="my-6"
src="/logo.svg"
width="128"
height="128"
alt="the Fresh logo: a sliced lemon dripping with juice"
/>
<h1 class="text-4xl font-bold">404 - Page not found</h1>
<p class="my-4">
The page you were looking for doesn't exist.
</p>
<a href="/" class="underline">Go back home</a>
</div>
</div>
</>
);
}

16
routes/_app.tsx Normal file
View File

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

21
routes/api/joke.ts Normal file
View File

@ -0,0 +1,21 @@
import { FreshContext } from "$fresh/server.ts";
// Jokes courtesy of https://punsandoneliners.com/randomness/programmer-jokes/
const JOKES = [
"Why do Java developers often wear glasses? They can't C#.",
"A SQL query walks into a bar, goes up to two tables and says “can I join you?”",
"Wasn't hard to crack Forrest Gump's password. 1forrest1.",
"I love pressing the F5 key. It's refreshing.",
"Called IT support and a chap from Australia came to fix my network connection. I asked “Do you come from a LAN down under?”",
"There are 10 types of people in the world. Those who understand binary and those who don't.",
"Why are assembly programmers often wet? They work below C level.",
"My favourite computer based band is the Black IPs.",
"What programme do you use to predict the music tastes of former US presidential candidates? An Al Gore Rhythm.",
"An SEO expert walked into a bar, pub, inn, tavern, hostelry, public house.",
];
export const handler = (_req: Request, _ctx: FreshContext): Response => {
const randomIndex = Math.floor(Math.random() * JOKES.length);
const body = JOKES[randomIndex];
return new Response(body);
};

5
routes/greet/[name].tsx Normal file
View File

@ -0,0 +1,5 @@
import { PageProps } from "$fresh/server.ts";
export default function Greet(props: PageProps) {
return <div>Hello {props.params.name}</div>;
}

25
routes/index.tsx Normal file
View File

@ -0,0 +1,25 @@
import { useSignal } from "@preact/signals";
import Counter from "../islands/Counter.tsx";
export default function Home() {
const count = useSignal(3);
return (
<div class="px-4 py-8 mx-auto bg-[#86efac]">
<div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
<img
class="my-6"
src="/logo.svg"
width="128"
height="128"
alt="the Fresh logo: a sliced lemon dripping with juice"
/>
<h1 class="text-4xl font-bold">Welcome to Fresh</h1>
<p class="my-4">
Try updating this message in the
<code class="mx-2">./routes/index.tsx</code> file, and refresh.
</p>
<Counter count={count} />
</div>
</div>
);
}

View File

@ -1,77 +1,77 @@
'use strict'; "use strict";
export const handler = async (event, context) => { export const handler = async (event, context) => {
const eventObj = JSON.parse(event); const eventObj = JSON.parse(event);
console.log(`receive event: ${JSON.stringify(eventObj)}`); console.log(`receive event: ${JSON.stringify(eventObj)}`);
let body = 'Missing parameter: URL'; let body = "Missing parameter: URL";
let statusCode = 400; let statusCode = 400;
// User-Agent list // User-Agent list
const userAgents = [ const userAgents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15', "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15",
'Mozilla/5.0 (Linux; Android 10; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36', "Mozilla/5.0 (Linux; Android 10; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36",
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1",
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/89.0' "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/89.0",
]; ];
// get http request body // get http request body
if ("body" in eventObj) { if ("body" in eventObj) {
body = eventObj.body; body = eventObj.body;
if (eventObj.isBase64Encoded) { if (eventObj.isBase64Encoded) {
body = Buffer.from(body, 'base64').toString('utf-8'); body = Buffer.from(body, "base64").toString("utf-8");
} }
} }
console.log(`receive http body: ${body}`); console.log(`receive http body: ${body}`);
// proxy the URL if it exists in eventObj // proxy the URL if it exists in eventObj
const refererUrl = 'https://www.bilibili.com/'; // Replace with your desired referer and origin const refererUrl = "https://www.bilibili.com/"; // Replace with your desired referer and origin
if ("url" in eventObj) { if ("url" in eventObj) {
try { try {
const randomUserAgent = userAgents[Math.floor(Math.random() * userAgents.length)]; const randomUserAgent = userAgents[Math.floor(Math.random() * userAgents.length)];
const response = await fetch(eventObj.url, { const response = await fetch(eventObj.url, {
headers: { headers: {
'User-Agent': randomUserAgent, "User-Agent": randomUserAgent,
'Referer': refererUrl "Referer": refererUrl,
} },
}); });
statusCode = response.status; statusCode = response.status;
body = await response.text(); body = await response.text();
} catch (error) { } catch (error) {
statusCode = 500; statusCode = 500;
body = `Error fetching URL: ${error.message}`; body = `Error fetching URL: ${error.message}`;
} }
} else if ("urls" in eventObj && Array.isArray(eventObj.urls)) { } else if ("urls" in eventObj && Array.isArray(eventObj.urls)) {
const requests = eventObj.urls.map(async url => { const requests = eventObj.urls.map(async (url) => {
try { try {
const randomUserAgent = userAgents[Math.floor(Math.random() * userAgents.length)]; const randomUserAgent = userAgents[Math.floor(Math.random() * userAgents.length)];
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
'User-Agent': randomUserAgent, "User-Agent": randomUserAgent,
'Referer': refererUrl "Referer": refererUrl,
} },
}); });
const responseBody = await response.text(); const responseBody = await response.text();
return { return {
statusCode: response.status, statusCode: response.status,
body: responseBody body: responseBody,
}; };
} catch (error) { } catch (error) {
return { return {
statusCode: 500, statusCode: 500,
body: `Error fetching URL: ${error.message}` body: `Error fetching URL: ${error.message}`,
}; };
} }
}); });
body = await Promise.all(requests); body = await Promise.all(requests);
statusCode = 200; // Assuming all URLs were processed successfully statusCode = 200; // Assuming all URLs were processed successfully
} }
return { return {
'statusCode': statusCode, "statusCode": statusCode,
'body': JSON.stringify(body) "body": JSON.stringify(body),
}; };
}; };

View File

@ -13,98 +13,98 @@ const db = new Database(DATABASE_PATH, { int64: true });
// 设置日志 // 设置日志
async function setupLogging() { async function setupLogging() {
await ensureDir(LOG_DIR); await ensureDir(LOG_DIR);
const logStream = await Deno.open(LOG_FILE, { write: true, create: true, append: true }); const logStream = await Deno.open(LOG_FILE, { write: true, create: true, append: true });
const redirectConsole = const redirectConsole =
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
(originalConsole: (...args: any[]) => void) => (originalConsole: (...args: any[]) => void) =>
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
(...args: any[]) => { (...args: any[]) => {
const message = args.map((arg) => (typeof arg === "object" ? JSON.stringify(arg) : arg)).join(" "); const message = args.map((arg) => (typeof arg === "object" ? JSON.stringify(arg) : arg)).join(" ");
originalConsole(message); originalConsole(message);
logStream.write(new TextEncoder().encode(message + "\n")); logStream.write(new TextEncoder().encode(message + "\n"));
}; };
console.log = redirectConsole(console.log); console.log = redirectConsole(console.log);
console.error = redirectConsole(console.error); console.error = redirectConsole(console.error);
console.warn = redirectConsole(console.warn); console.warn = redirectConsole(console.warn);
} }
interface Metadata { interface Metadata {
key: string; key: string;
value: string; value: string;
} }
// 获取最后一次更新的时间 // 获取最后一次更新的时间
function getLastUpdate(): Date { function getLastUpdate(): Date {
const result = db.prepare("SELECT value FROM metadata WHERE key = 'fetchAid-lastUpdate'").get() as Metadata; const result = db.prepare("SELECT value FROM metadata WHERE key = 'fetchAid-lastUpdate'").get() as Metadata;
return result ? new Date(result.value as string) : new Date(0); return result ? new Date(result.value as string) : new Date(0);
} }
// 更新最后更新时间 // 更新最后更新时间
function updateLastUpdate() { function updateLastUpdate() {
const now = new Date().toISOString(); const now = new Date().toISOString();
db.prepare("UPDATE metadata SET value = ? WHERE key = 'fetchAid-lastUpdate'").run(now); db.prepare("UPDATE metadata SET value = ? WHERE key = 'fetchAid-lastUpdate'").run(now);
} }
// 辅助函数:获取数据 // 辅助函数:获取数据
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
async function fetchData(pn: number, retries = MAX_RETRIES): Promise<any> { async function fetchData(pn: number, retries = MAX_RETRIES): Promise<any> {
try { try {
const response = await fetch(`${API_URL}${pn}`); const response = await fetch(`${API_URL}${pn}`);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return await response.json(); return await response.json();
} catch (error) { } catch (error) {
if (retries > 0) { if (retries > 0) {
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
return fetchData(pn, retries - 1); return fetchData(pn, retries - 1);
} }
throw error; throw error;
} }
} }
// 插入 aid 到数据库 // 插入 aid 到数据库
function insertAid(aid: number) { function insertAid(aid: number) {
db.prepare("INSERT OR IGNORE INTO bili_info_crawl (aid, status) VALUES (?, 'pending')").run(aid); db.prepare("INSERT OR IGNORE INTO bili_info_crawl (aid, status) VALUES (?, 'pending')").run(aid);
} }
// 主函数 // 主函数
async function main() { async function main() {
await setupLogging(); await setupLogging();
let pn = 1; let pn = 1;
let shouldContinue = true; let shouldContinue = true;
const lastUpdate = getLastUpdate(); const lastUpdate = getLastUpdate();
while (shouldContinue) { while (shouldContinue) {
try { try {
const data = await fetchData(pn); const data = await fetchData(pn);
const archives = data.data.archives; const archives = data.data.archives;
for (const archive of archives) { for (const archive of archives) {
const pubTime = new Date(archive.pubdate * 1000); const pubTime = new Date(archive.pubdate * 1000);
if (pubTime > lastUpdate) { if (pubTime > lastUpdate) {
insertAid(archive.aid); insertAid(archive.aid);
} else { } else {
shouldContinue = false; shouldContinue = false;
break; break;
} }
} }
pn++; pn++;
console.log(`Fetched page ${pn}`); console.log(`Fetched page ${pn}`);
} catch (error) { } catch (error) {
console.error(`Error fetching data for pn=${pn}: ${error}`); console.error(`Error fetching data for pn=${pn}: ${error}`);
} }
} }
// 更新最后更新时间 // 更新最后更新时间
updateLastUpdate(); updateLastUpdate();
// 关闭数据库 // 关闭数据库
db.close(); db.close();
} }
// 运行主函数 // 运行主函数
main().catch(console.error); main().catch(console.error);

View File

@ -17,127 +17,127 @@ const MINUTES = MINUTE;
const IPs = regions.length; const IPs = regions.length;
const rateLimits = [ const rateLimits = [
{ window: 5 * MINUTES, maxRequests: 160 * IPs }, { window: 5 * MINUTES, maxRequests: 160 * IPs },
{ window: 30 * SECONDS, maxRequests: 20 * IPs }, { window: 30 * SECONDS, maxRequests: 20 * IPs },
{ window: 1.2 * SECOND, maxRequests: 1 * IPs }, { window: 1.2 * SECOND, maxRequests: 1 * IPs },
]; ];
const requestQueue: number[] = []; const requestQueue: number[] = [];
async function setupLogging() { async function setupLogging() {
await ensureDir(logDir); await ensureDir(logDir);
const logStream = await Deno.open(logFile, { write: true, create: true, append: true }); const logStream = await Deno.open(logFile, { write: true, create: true, append: true });
const redirectConsole = const redirectConsole =
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
(originalConsole: (...args: any[]) => void) => (originalConsole: (...args: any[]) => void) =>
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
(...args: any[]) => { (...args: any[]) => {
const message = args.map((arg) => (typeof arg === "object" ? JSON.stringify(arg) : arg)).join(" "); const message = args.map((arg) => (typeof arg === "object" ? JSON.stringify(arg) : arg)).join(" ");
originalConsole(message); originalConsole(message);
logStream.write(new TextEncoder().encode(message + "\n")); logStream.write(new TextEncoder().encode(message + "\n"));
}; };
console.log = redirectConsole(console.log); console.log = redirectConsole(console.log);
console.error = redirectConsole(console.error); console.error = redirectConsole(console.error);
console.warn = redirectConsole(console.warn); console.warn = redirectConsole(console.warn);
} }
function isRateLimited(): boolean { function isRateLimited(): boolean {
const now = Date.now(); const now = Date.now();
return rateLimits.some(({ window, maxRequests }) => { return rateLimits.some(({ window, maxRequests }) => {
const windowStart = now - window; const windowStart = now - window;
const requestsInWindow = requestQueue.filter((timestamp) => timestamp >= windowStart).length; const requestsInWindow = requestQueue.filter((timestamp) => timestamp >= windowStart).length;
return requestsInWindow >= maxRequests; return requestsInWindow >= maxRequests;
}); });
} }
async function readFromText() { async function readFromText() {
const aidRawcontent = await Deno.readTextFile(aidPath); const aidRawcontent = await Deno.readTextFile(aidPath);
const aids = aidRawcontent const aids = aidRawcontent
.split("\n") .split("\n")
.filter((line) => line.length > 0) .filter((line) => line.length > 0)
.map((line) => parseInt(line)); .map((line) => parseInt(line));
// if (!db.prepare("SELECT COUNT(*) FROM bili_info_crawl").get()) { // if (!db.prepare("SELECT COUNT(*) FROM bili_info_crawl").get()) {
// const insertStmt = db.prepare("INSERT OR IGNORE INTO bili_info_crawl (aid, status) VALUES (?, 'pending')"); // const insertStmt = db.prepare("INSERT OR IGNORE INTO bili_info_crawl (aid, status) VALUES (?, 'pending')");
// aids.forEach((aid) => insertStmt.run(aid)); // aids.forEach((aid) => insertStmt.run(aid));
// } // }
// 查询数据库中已经存在的 aid // 查询数据库中已经存在的 aid
const existingAids = db const existingAids = db
.prepare("SELECT aid FROM bili_info_crawl") .prepare("SELECT aid FROM bili_info_crawl")
.all() .all()
.map((row) => row.aid); .map((row) => row.aid);
console.log(existingAids.length); console.log(existingAids.length);
// 将 existingAids 转换为 Set 以提高查找效率 // 将 existingAids 转换为 Set 以提高查找效率
const existingAidsSet = new Set(existingAids); const existingAidsSet = new Set(existingAids);
// 找出 aids 数组中不存在于数据库的条目 // 找出 aids 数组中不存在于数据库的条目
const newAids = aids.filter((aid) => !existingAidsSet.has(aid)); const newAids = aids.filter((aid) => !existingAidsSet.has(aid));
// 插入这些新条目 // 插入这些新条目
const insertStmt = db.prepare("INSERT OR IGNORE INTO bili_info_crawl (aid, status) VALUES (?, 'pending')"); const insertStmt = db.prepare("INSERT OR IGNORE INTO bili_info_crawl (aid, status) VALUES (?, 'pending')");
newAids.forEach((aid) => insertStmt.run(aid)); newAids.forEach((aid) => insertStmt.run(aid));
} }
async function insertAidsToDB() { async function insertAidsToDB() {
if (shouldReadTextFile) { if (shouldReadTextFile) {
await readFromText(); await readFromText();
} }
const aidsInDB = db const aidsInDB = db
.prepare("SELECT aid FROM bili_info_crawl WHERE status = 'pending' OR status = 'failed'") .prepare("SELECT aid FROM bili_info_crawl WHERE status = 'pending' OR status = 'failed'")
.all() .all()
.map((row) => row.aid) as number[]; .map((row) => row.aid) as number[];
const totalAids = aidsInDB.length; const totalAids = aidsInDB.length;
let processedAids = 0; let processedAids = 0;
const startTime = Date.now(); const startTime = Date.now();
const processAid = async (aid: number) => { const processAid = async (aid: number) => {
try { try {
const res = await getBiliBiliVideoInfo(aid, regions[processedAids % regions.length]); const res = await getBiliBiliVideoInfo(aid, regions[processedAids % regions.length]);
if (res === null) { if (res === null) {
updateAidStatus(aid, "failed"); updateAidStatus(aid, "failed");
} else { } else {
const rawData = JSON.parse(res); const rawData = JSON.parse(res);
if (rawData.code === 0) { if (rawData.code === 0) {
updateAidStatus(aid, "success", rawData.data.View.bvid, JSON.stringify(rawData.data)); updateAidStatus(aid, "success", rawData.data.View.bvid, JSON.stringify(rawData.data));
} else { } else {
updateAidStatus(aid, "error", undefined, res); updateAidStatus(aid, "error", undefined, res);
} }
} }
} catch (error) { } catch (error) {
console.error(`Error updating aid ${aid}: ${error}`); console.error(`Error updating aid ${aid}: ${error}`);
updateAidStatus(aid, "failed"); updateAidStatus(aid, "failed");
} finally { } finally {
processedAids++; processedAids++;
logProgress(aid, processedAids, totalAids, startTime); logProgress(aid, processedAids, totalAids, startTime);
} }
}; };
const interval = setInterval(async () => { const interval = setInterval(async () => {
if (aidsInDB.length === 0) { if (aidsInDB.length === 0) {
clearInterval(interval); clearInterval(interval);
console.log("All aids processed."); console.log("All aids processed.");
return; return;
} }
if (!isRateLimited()) { if (!isRateLimited()) {
const aid = aidsInDB.shift(); const aid = aidsInDB.shift();
if (aid !== undefined) { if (aid !== undefined) {
requestQueue.push(Date.now()); requestQueue.push(Date.now());
await processAid(aid); await processAid(aid);
} }
} }
}, 50); }, 50);
console.log("Starting to process aids..."); console.log("Starting to process aids...");
} }
function updateAidStatus(aid: number, status: string, bvid?: string, data?: string) { function updateAidStatus(aid: number, status: string, bvid?: string, data?: string) {
const stmt = db.prepare(` const stmt = db.prepare(`
UPDATE bili_info_crawl UPDATE bili_info_crawl
SET status = ?, SET status = ?,
${bvid ? "bvid = ?," : ""} ${bvid ? "bvid = ?," : ""}
@ -145,31 +145,35 @@ function updateAidStatus(aid: number, status: string, bvid?: string, data?: stri
timestamp = ? timestamp = ?
WHERE aid = ? WHERE aid = ?
`); `);
const params = [status, ...(bvid ? [bvid] : []), ...(data ? [data] : []), Date.now() / 1000, aid]; const params = [status, ...(bvid ? [bvid] : []), ...(data ? [data] : []), Date.now() / 1000, aid];
stmt.run(...params); stmt.run(...params);
} }
function logProgress(aid: number, processedAids: number, totalAids: number, startTime: number) { function logProgress(aid: number, processedAids: number, totalAids: number, startTime: number) {
const elapsedTime = Date.now() - startTime; const elapsedTime = Date.now() - startTime;
const elapsedSeconds = Math.floor(elapsedTime / 1000); const elapsedSeconds = Math.floor(elapsedTime / 1000);
const elapsedMinutes = Math.floor(elapsedSeconds / 60); const elapsedMinutes = Math.floor(elapsedSeconds / 60);
const elapsedHours = Math.floor(elapsedMinutes / 60); const elapsedHours = Math.floor(elapsedMinutes / 60);
const remainingAids = totalAids - processedAids; const remainingAids = totalAids - processedAids;
const averageTimePerAid = elapsedTime / processedAids; const averageTimePerAid = elapsedTime / processedAids;
const eta = remainingAids * averageTimePerAid; const eta = remainingAids * averageTimePerAid;
const etaSeconds = Math.floor(eta / 1000); const etaSeconds = Math.floor(eta / 1000);
const etaMinutes = Math.floor(etaSeconds / 60); const etaMinutes = Math.floor(etaSeconds / 60);
const etaHours = Math.floor(etaMinutes / 60); const etaHours = Math.floor(etaMinutes / 60);
const progress = `${processedAids}/${totalAids}, ${((processedAids / totalAids) * 100).toFixed( const progress = `${processedAids}/${totalAids}, ${
2 ((processedAids / totalAids) * 100).toFixed(
)}%, elapsed ${elapsedHours.toString().padStart(2, "0")}:${(elapsedMinutes % 60).toString().padStart(2, "0")}:${( 2,
elapsedSeconds % 60 )
) }%, elapsed ${elapsedHours.toString().padStart(2, "0")}:${(elapsedMinutes % 60).toString().padStart(2, "0")}:${
.toString() (
.padStart(2, "0")}, ETA ${etaHours}h${(etaMinutes % 60).toString().padStart(2, "0")}m`; elapsedSeconds % 60
console.log(`Updated aid ${aid}, ${progress}`); )
.toString()
.padStart(2, "0")
}, ETA ${etaHours}h${(etaMinutes % 60).toString().padStart(2, "0")}m`;
console.log(`Updated aid ${aid}, ${progress}`);
} }
await setupLogging(); await setupLogging();

View File

@ -1,53 +1,51 @@
export async function getBiliBiliVideoInfo(bvidORaid?: string | number, region: string = "hangzhou") { export async function getBiliBiliVideoInfo(bvidORaid?: string | number, region: string = "hangzhou") {
const bvid = typeof bvidORaid === "string" ? bvidORaid : undefined; const bvid = typeof bvidORaid === "string" ? bvidORaid : undefined;
const aid = typeof bvidORaid === "number" ? bvidORaid : undefined; const aid = typeof bvidORaid === "number" ? bvidORaid : undefined;
const baseURL = "https://api.bilibili.com/x/web-interface/view/detail"; const baseURL = "https://api.bilibili.com/x/web-interface/view/detail";
const urlObject = new URL(baseURL); const urlObject = new URL(baseURL);
if (aid) { if (aid) {
urlObject.searchParams.append("aid", aid.toString()); urlObject.searchParams.append("aid", aid.toString());
const finalURL = urlObject.toString(); const finalURL = urlObject.toString();
return await proxyRequestWithRegion(finalURL, region); return await proxyRequestWithRegion(finalURL, region);
} else if (bvid) { } else if (bvid) {
urlObject.searchParams.append("bvid", bvid); urlObject.searchParams.append("bvid", bvid);
const finalURL = urlObject.toString(); const finalURL = urlObject.toString();
return await proxyRequestWithRegion(finalURL, region); return await proxyRequestWithRegion(finalURL, region);
} else { } else {
return null; return null;
} }
} }
async function proxyRequestWithRegion(url: string, region: string): Promise<any | null> { async function proxyRequestWithRegion(url: string, region: string): Promise<any | null> {
const td = new TextDecoder(); const td = new TextDecoder();
const p = await new Deno.Command("aliyun", { const p = await new Deno.Command("aliyun", {
args: [ args: [
"fc", "fc",
"POST", "POST",
`/2023-03-30/functions/proxy-${region}/invocations`, `/2023-03-30/functions/proxy-${region}/invocations`,
"--qualifier", "--qualifier",
"LATEST", "LATEST",
"--header", "--header",
"Content-Type=application/json;x-fc-invocation-type=Sync;x-fc-log-type=None;", "Content-Type=application/json;x-fc-invocation-type=Sync;x-fc-log-type=None;",
"--body", "--body",
JSON.stringify({url: url}), JSON.stringify({ url: url }),
"--profile", "--profile",
`CVSA-${region}`, `CVSA-${region}`,
], ],
}).output(); }).output();
try { try {
const out = td.decode(p.stdout); const out = td.decode(p.stdout);
const rawData = JSON.parse(out); const rawData = JSON.parse(out);
if (rawData.statusCode !== 200) { if (rawData.statusCode !== 200) {
console.error(`Error proxying request ${url} to ${region} , statusCode: ${rawData.statusCode}`); console.error(`Error proxying request ${url} to ${region} , statusCode: ${rawData.statusCode}`);
return null; return null;
} } else {
else { return JSON.parse(rawData.body);
return JSON.parse(rawData.body); }
} } catch (e) {
} console.error(`Error proxying request ${url} to ${region}: ${e}`);
catch (e){ return null;
console.error(`Error proxying request ${url} to ${region}: ${e}`); }
return null; }
}
}

View File

@ -1,3 +1,35 @@
import { Client } from "https://deno.land/x/postgres@v0.19.3/mod.ts";
const API_URL = "https://api.bilibili.com/x/web-interface/newlist?rid=30&ps=50&pn=";
const requiredEnvVars = ["DB_HOST", "DB_NAME", "DB_USER", "DB_PASSWORD", "DB_PORT"];
const unsetVars = requiredEnvVars.filter((key) => !Deno.env.get(key));
if (unsetVars.length > 0) {
throw new Error(`Missing required environment variables: ${unsetVars.join(", ")}`);
}
const databaseHost = Deno.env.get("DB_HOST")!;
const databaseName = Deno.env.get("DB_NAME")!;
const databaseUser = Deno.env.get("DB_USER")!;
const databasePassword = Deno.env.get("DB_PASSWORD")!;
const databasePort = Deno.env.get("DB_PORT")!;
const postgresConfig = {
hostname: databaseHost,
port: parseInt(databasePort),
database: databaseName,
user: databaseUser,
password: databasePassword,
};
async function connectToPostgres() {
const client = new Client(postgresConfig);
await client.connect();
return client;
}
export async function getLatestVideos() { export async function getLatestVideos() {
const baseURL = "" const client = await connectToPostgres();
} }

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

6
static/logo.svg Normal file
View File

@ -0,0 +1,6 @@
<svg width="40" height="40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M34.092 8.845C38.929 20.652 34.092 27 30 30.5c1 3.5-2.986 4.222-4.5 2.5-4.457 1.537-13.512 1.487-20-5C2 24.5 4.73 16.714 14 11.5c8-4.5 16-7 20.092-2.655Z" fill="#FFDB1E"/>
<path d="M14 11.5c6.848-4.497 15.025-6.38 18.368-3.47C37.5 12.5 21.5 22.612 15.5 25c-6.5 2.587-3 8.5-6.5 8.5-3 0-2.5-4-5.183-7.75C2.232 23.535 6.16 16.648 14 11.5Z" fill="#fff" stroke="#FFDB1E"/>
<path d="M28.535 8.772c4.645 1.25-.365 5.695-4.303 8.536-3.732 2.692-6.606 4.21-7.923 4.83-.366.173-1.617-2.252-1.617-1 0 .417-.7 2.238-.934 2.326-1.365.512-4.223 1.29-5.835 1.29-3.491 0-1.923-4.754 3.014-9.122.892-.789 1.478-.645 2.283-.645-.537-.773-.534-.917.403-1.546C17.79 10.64 23 8.77 25.212 8.42c.366.014.82.35.82.629.41-.14 2.095-.388 2.503-.278Z" fill="#FFE600"/>
<path d="M14.297 16.49c.985-.747 1.644-1.01 2.099-2.526.566.121.841-.08 1.29-.701.324.466 1.657.608 2.453.701-.715.451-1.057.852-1.452 2.106-1.464-.611-3.167-.302-4.39.42Z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

3
static/styles.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

7
tailwind.config.ts Normal file
View File

@ -0,0 +1,7 @@
import { type Config } from "tailwindcss";
export default {
content: [
"{routes,islands,components}/**/*.{ts,tsx,js,jsx}",
],
} satisfies Config;