mirror of
https://github.com/zoriya/Kyoo.git
synced 2026-02-20 10:10:08 -05:00
Make tabs part of the navbar on web (#1318)
This commit is contained in:
commit
104f1fc599
124
api/bun.lock
124
api/bun.lock
@ -13,28 +13,28 @@
|
||||
"@logtape/otel": "2.0.2",
|
||||
"@logtape/redaction": "2.0.2",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/api-logs": "^0.211.0",
|
||||
"@opentelemetry/core": "^2.5.0",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.211.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.211.0",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.211.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.211.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.211.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.211.0",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.211.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.211.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.211.0",
|
||||
"@opentelemetry/resources": "^2.5.0",
|
||||
"@opentelemetry/sdk-logs": "^0.211.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.5.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.5.0",
|
||||
"@opentelemetry/sdk-trace-node": "^2.5.0",
|
||||
"@opentelemetry/api-logs": "^0.212.0",
|
||||
"@opentelemetry/core": "^2.5.1",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.212.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.212.0",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.212.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.212.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.212.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.212.0",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.212.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.212.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.212.0",
|
||||
"@opentelemetry/resources": "^2.5.1",
|
||||
"@opentelemetry/sdk-logs": "^0.212.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.5.1",
|
||||
"@opentelemetry/sdk-trace-base": "^2.5.1",
|
||||
"@opentelemetry/sdk-trace-node": "^2.5.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.39.0",
|
||||
"@types/bun": "^1.3.6",
|
||||
"@types/bun": "^1.3.9",
|
||||
"blurhash": "^2.0.5",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"drizzle-orm": "0.44.7",
|
||||
"elysia": "^1.4.23",
|
||||
"elysia": "^1.4.25",
|
||||
"jose": "^6.1.3",
|
||||
"node-addon-api": "^8.5.0",
|
||||
"parjs": "^1.3.9",
|
||||
@ -42,7 +42,7 @@
|
||||
"sharp": "^0.34.5",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.3.14",
|
||||
"@biomejs/biome": "2.4.0",
|
||||
"@types/pg": "^8.16.0",
|
||||
},
|
||||
},
|
||||
@ -51,23 +51,23 @@
|
||||
"drizzle-orm@0.44.7": "patches/drizzle-orm@0.44.7.patch",
|
||||
},
|
||||
"packages": {
|
||||
"@biomejs/biome": ["@biomejs/biome@2.3.14", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.14", "@biomejs/cli-darwin-x64": "2.3.14", "@biomejs/cli-linux-arm64": "2.3.14", "@biomejs/cli-linux-arm64-musl": "2.3.14", "@biomejs/cli-linux-x64": "2.3.14", "@biomejs/cli-linux-x64-musl": "2.3.14", "@biomejs/cli-win32-arm64": "2.3.14", "@biomejs/cli-win32-x64": "2.3.14" }, "bin": { "biome": "bin/biome" } }, "sha512-QMT6QviX0WqXJCaiqVMiBUCr5WRQ1iFSjvOLoTk6auKukJMvnMzWucXpwZB0e8F00/1/BsS9DzcKgWH+CLqVuA=="],
|
||||
"@biomejs/biome": ["@biomejs/biome@2.4.0", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.0", "@biomejs/cli-darwin-x64": "2.4.0", "@biomejs/cli-linux-arm64": "2.4.0", "@biomejs/cli-linux-arm64-musl": "2.4.0", "@biomejs/cli-linux-x64": "2.4.0", "@biomejs/cli-linux-x64-musl": "2.4.0", "@biomejs/cli-win32-arm64": "2.4.0", "@biomejs/cli-win32-x64": "2.4.0" }, "bin": { "biome": "bin/biome" } }, "sha512-iluT61cORUDIC5i/y42ljyQraCemmmcgbMLLCnYO+yh+2hjTmcMFcwY8G0zTzWCsPb3t3AyKc+0t/VuhPZULUg=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UJGPpvWJMkLxSRtpCAKfKh41Q4JJXisvxZL8ChN1eNW3m/WlPFJ6EFDCE7YfUb4XS8ZFi3C1dFpxUJ0Ety5n+A=="],
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-L+YpOtPSuU0etomfvFTPWRsa7+8ejaJL3yaROEoT/96HDJbR6OsvZQk0C8JUYou+XFdP+JcGxqZknkp4n934RA=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-PNkLNQG6RLo8lG7QoWe/hhnMxJIt1tEimoXpGQjwS/dkdNiKBLPv4RpeQl8o3s1OKI3ZOR5XPiYtmbGGHAOnLA=="],
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Aq+S7ffpb5ynTyLgtnEjG+W6xuTd2F7FdC7J6ShpvRhZwJhjzwITGF9vrqoOnw0sv1XWkt2Q1Rpg+hleg/Xg7Q=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-KT67FKfzIw6DNnUNdYlBg+eU24Go3n75GWK6NwU4+yJmDYFe9i/MjiI+U/iEzKvo0g7G7MZqoyrhIYuND2w8QQ=="],
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-u2p54IhvNAWB+h7+rxCZe3reNfQYFK+ppDw+q0yegrGclFYnDPZAntv/PqgUacpC3uxTeuWFgWW7RFe3lHuxOA=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-LInRbXhYujtL3sH2TMCH/UBwJZsoGwfQjBrMfl84CD4hL/41C/EU5mldqf1yoFpsI0iPWuU83U+nB2TUUypWeg=="],
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-1rhDUq8sf7xX3tg7vbnU3WVfanKCKi40OXc4VleBMzRStmQHdeBY46aFP6VdwEomcVjyNiu+Zcr3LZtAdrZrjQ=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-ZsZzQsl9U+wxFrGGS4f6UxREUlgHwmEfu1IrXlgNFrNnd5Th6lIJr8KmSzu/+meSa9f4rzFrbEW9LBBA6ScoMA=="],
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-WVFOhsnzhrbMGOSIcB9yFdRV2oG2KkRRhIZiunI9gJqSU3ax9ErdnTxRfJUxZUI9NbzVxC60OCXNcu+mXfF/Tw=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-KQU7EkbBBuHPW3/rAcoiVmhlPtDSGOGRPv9js7qJVpYTzjQmVR+C9Rfcz+ti8YCH+zT1J52tuBybtP4IodjxZQ=="],
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Omo0xhl63z47X+CrE5viEWKJhejJyndl577VoXg763U/aoATrK3r5+8DPh02GokWPeODX1Hek00OtjjooGan9w=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-+IKYkj/pUBbnRf1G1+RlyA3LWiDgra1xpS7H2g4BuOzzRbRB+hmlw0yFsLprHhbbt7jUzbzAbAjK/Pn0FDnh1A=="],
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-aqRwW0LJLV1v1NzyLvLWQhdLmDSAV1vUh+OBdYJaa8f28XBn5BZavo+WTfqgEzALZxlNfBmu6NGO6Al3MbCULw=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.14", "", { "os": "win32", "cpu": "x64" }, "sha512-oizCjdyQ3WJEswpb3Chdngeat56rIdSYK12JI3iI11Mt5T5EXcZ7WLuowzEaFPNJ3zmOQFliMN8QY1Pi+qsfdQ=="],
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.0", "", { "os": "win32", "cpu": "x64" }, "sha512-g47s+V+OqsGxbSZN3lpav6WYOk0PIc3aCBAq+p6dwSynL3K5MA6Cg6nkzDOlu28GEHwbakW+BllzHCJCxnfK5Q=="],
|
||||
|
||||
"@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="],
|
||||
|
||||
@ -203,31 +203,31 @@
|
||||
|
||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||
|
||||
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.211.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg=="],
|
||||
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.212.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-TEEVrLbNROUkYY51sBJGk7lO/OLjuepch8+hmpM6ffMJQ2z/KVCjdHuCFX6fJj8OkJP2zckPjrJzQtXU3IAsFg=="],
|
||||
|
||||
"@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.5.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw=="],
|
||||
"@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.5.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw=="],
|
||||
|
||||
"@opentelemetry/core": ["@opentelemetry/core@2.5.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ=="],
|
||||
"@opentelemetry/core": ["@opentelemetry/core@2.5.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA=="],
|
||||
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.211.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-grpc-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/sdk-logs": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-UhOoWENNqyaAMP/dL1YXLkXt6ZBtovkDDs1p4rxto9YwJX1+wMjwg+Obfyg2kwpcMoaiIFT3KQIcLNW8nNGNfQ=="],
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.212.0", "", { "dependencies": { "@grpc/grpc-js": "^1.14.3", "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-grpc-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/sdk-logs": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-/0bk6fQG+eSFZ4L6NlckGTgUous/ib5+OVdg0x4OdwYeHzV3lTEo3it1HgnPY6UKpmX7ki+hJvxjsOql8rCeZA=="],
|
||||
|
||||
"@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.211.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.211.0", "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/sdk-logs": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-c118Awf1kZirHkqxdcF+rF5qqWwNjJh+BB1CmQvN9AQHC/DUIldy6dIkJn3EKlQnQ3HmuNRKc/nHHt5IusN7mA=="],
|
||||
"@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.212.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.212.0", "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/sdk-logs": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-JidJasLwG/7M9RTxV/64xotDKmFAUSBc9SNlxI32QYuUMK5rVKhHNWMPDzC7E0pCAL3cu+FyiKvsTwLi2KqPYw=="],
|
||||
|
||||
"@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.211.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.211.0", "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/sdk-logs": "0.211.0", "@opentelemetry/sdk-trace-base": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-kMvfKMtY5vJDXeLnwhrZMEwhZ2PN8sROXmzacFU/Fnl4Z79CMrOaL7OE+5X3SObRYlDUa7zVqaXp9ZetYCxfDQ=="],
|
||||
"@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.212.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.212.0", "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/resources": "2.5.1", "@opentelemetry/sdk-logs": "0.212.0", "@opentelemetry/sdk-trace-base": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-RpKB5UVfxc7c6Ta1UaCrxXDTQ0OD7BCGT66a97Q5zR1x3+9fw4dSaiqMXT/6FAWj2HyFbem6Rcu1UzPZikGTWQ=="],
|
||||
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": ["@opentelemetry/exporter-metrics-otlp-grpc@0.211.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.5.0", "@opentelemetry/exporter-metrics-otlp-http": "0.211.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-grpc-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/sdk-metrics": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-D/U3G8L4PzZp8ot5hX9wpgbTymgtLZCiwR7heMe4LsbGV4OdctS1nfyvaQHLT6CiGZ6FjKc1Vk9s6kbo9SWLXQ=="],
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": ["@opentelemetry/exporter-metrics-otlp-grpc@0.212.0", "", { "dependencies": { "@grpc/grpc-js": "^1.14.3", "@opentelemetry/core": "2.5.1", "@opentelemetry/exporter-metrics-otlp-http": "0.212.0", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-grpc-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/resources": "2.5.1", "@opentelemetry/sdk-metrics": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-/6Gqf9wpBq22XsomR1i0iPGnbQtCq2Vwnrq5oiDPjYSqveBdK1jtQbhGfmpK2mLLxk4cPDtD1ZEYdIou5K8EaA=="],
|
||||
|
||||
"@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/sdk-metrics": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-lfHXElPAoDSPpPO59DJdN5FLUnwi1wxluLTWQDayqrSPfWRnluzxRhD+g7rF8wbj1qCz0sdqABl//ug1IZyWvA=="],
|
||||
"@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/resources": "2.5.1", "@opentelemetry/sdk-metrics": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-8hgBw3aTTRpSTkU4b9MLf/2YVLnfWp+hfnLq/1Fa2cky+vx6HqTodo+Zv1GTIrAKMOOwgysOjufy0gTxngqeBg=="],
|
||||
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": ["@opentelemetry/exporter-metrics-otlp-proto@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/exporter-metrics-otlp-http": "0.211.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/sdk-metrics": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-61iNbffEpyZv/abHaz3BQM3zUtA2kVIDBM+0dS9RK68ML0QFLRGYa50xVMn2PYMToyfszEPEgFC3ypGae2z8FA=="],
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": ["@opentelemetry/exporter-metrics-otlp-proto@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/exporter-metrics-otlp-http": "0.212.0", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/resources": "2.5.1", "@opentelemetry/sdk-metrics": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-C7I4WN+ghn3g7SnxXm2RK3/sRD0k/BYcXaK6lGU3yPjiM7a1M25MLuM6zY3PeVPPzzTZPfuS7+wgn/tHk768Xw=="],
|
||||
|
||||
"@opentelemetry/exporter-prometheus": ["@opentelemetry/exporter-prometheus@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ZYdlU9r0USuuYppiDyU2VFRA0kFl855ylnb3N/2aOlXrbA4PMCznen7gmPbetGQu7pz8Jbaf4fwvrDnVdQQXSw=="],
|
||||
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.211.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-grpc-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/sdk-trace-base": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-eFwx4Gvu6LaEiE1rOd4ypgAiWEdZu7Qzm2QNN2nJqPW1XDeAVH1eNwVcVQl+QK9HR/JCDZ78PZgD7xD/DBDqbw=="],
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.212.0", "", { "dependencies": { "@grpc/grpc-js": "^1.14.3", "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-grpc-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/resources": "2.5.1", "@opentelemetry/sdk-trace-base": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-9xTuYWp8ClBhljDGAoa0NSsJcsxJsC9zCFKMSZJp1Osb9pjXCMRdA6fwXtlubyqe7w8FH16EWtQNKx/FWi+Ghw=="],
|
||||
|
||||
"@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/sdk-trace-base": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-F1Rv3JeMkgS//xdVjbQMrI3+26e5SXC7vXA6trx8SWEA0OUhw4JHB+qeHtH0fJn46eFItrYbL5m8j4qi9Sfaxw=="],
|
||||
"@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/resources": "2.5.1", "@opentelemetry/sdk-trace-base": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-v/0wMozNoiEPRolzC4YoPo4rAT0q8r7aqdnRw3Nu7IDN0CGFzNQazkfAlBJ6N5y0FYJkban7Aw5WnN73//6YlA=="],
|
||||
|
||||
"@opentelemetry/exporter-trace-otlp-proto": ["@opentelemetry/exporter-trace-otlp-proto@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/sdk-trace-base": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DkjXwbPiqpcPlycUojzG2RmR0/SIK8Gi9qWO9znNvSqgzrnAIE9x2n6yPfpZ+kWHZGafvsvA1lVXucTyyQa5Kg=="],
|
||||
"@opentelemetry/exporter-trace-otlp-proto": ["@opentelemetry/exporter-trace-otlp-proto@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0", "@opentelemetry/resources": "2.5.1", "@opentelemetry/sdk-trace-base": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-d1ivqPT0V+i0IVOOdzGaLqonjtlk5jYrW7ItutWzXL/Mk+PiYb59dymy/i2reot9dDnBFWfrsvxyqdutGF5Vig=="],
|
||||
|
||||
"@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-icxaKZ+jZL/NHXX8Aru4HGsrdhK0MLcuRXkX5G5IRmCgoRLw+Br6I/nMVozX2xjGGwV7hw2g+4Slj8K7s4HbVg=="],
|
||||
|
||||
@ -235,25 +235,25 @@
|
||||
|
||||
"@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.208.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA=="],
|
||||
|
||||
"@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.211.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-exporter-base": "0.211.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-mR5X+N4SuphJeb7/K7y0JNMC8N1mB6gEtjyTLv+TSAhl0ZxNQzpSKP8S5Opk90fhAqVYD4R0SQSAirEBlH1KSA=="],
|
||||
"@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.212.0", "", { "dependencies": { "@grpc/grpc-js": "^1.14.3", "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-exporter-base": "0.212.0", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-YidOSlzpsun9uw0iyIWrQp6HxpMtBlECE3tiHGAsnpEqJWbAUWcMnIffvIuvTtTQ1OyRtwwaE79dWSQ8+eiB7g=="],
|
||||
|
||||
"@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.211.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.211.0", "@opentelemetry/core": "2.5.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/sdk-logs": "0.211.0", "@opentelemetry/sdk-metrics": "2.5.0", "@opentelemetry/sdk-trace-base": "2.5.0", "protobufjs": "8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-julhCJ9dXwkOg9svuuYqqjXLhVaUgyUvO2hWbTxwjvLXX2rG3VtAaB0SzxMnGTuoCZizBT7Xqqm2V7+ggrfCXA=="],
|
||||
"@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.212.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.212.0", "@opentelemetry/core": "2.5.1", "@opentelemetry/resources": "2.5.1", "@opentelemetry/sdk-logs": "0.212.0", "@opentelemetry/sdk-metrics": "2.5.1", "@opentelemetry/sdk-trace-base": "2.5.1", "protobufjs": "8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bj7zYFOg6Db7NUwsRZQ/WoVXpAf41WY2gsd3kShSfdpZQDRKHWJiRZIg7A8HvWsf97wb05rMFzPbmSHyjEl9tw=="],
|
||||
|
||||
"@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-blx9S2EI49Ycuw6VZq+bkpaIoiJFhsDuvFGhBIoH3vJ5oYjJ2U0s3fAM5jYft99xVIAv6HqoPtlP9gpVA2IZtA=="],
|
||||
|
||||
"@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Mbm/LSFyAtQKP0AQah4AfGgsD+vsZcyreZoQ5okFBk33hU7AquU4TltgyL9dvaO8/Zkoud8/0gEvwfOZ5d7EPA=="],
|
||||
|
||||
"@opentelemetry/resources": ["@opentelemetry/resources@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g=="],
|
||||
"@opentelemetry/resources": ["@opentelemetry/resources@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ=="],
|
||||
|
||||
"@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.211.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.211.0", "@opentelemetry/core": "2.5.0", "@opentelemetry/resources": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-O5nPwzgg2JHzo59kpQTPUOTzFi0Nv5LxryG27QoXBciX3zWM3z83g+SNOHhiQVYRWFSxoWn1JM2TGD5iNjOwdA=="],
|
||||
"@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.212.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.212.0", "@opentelemetry/core": "2.5.1", "@opentelemetry/resources": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-qglb5cqTf0mOC1sDdZ7nfrPjgmAqs2OxkzOPIf2+Rqx8yKBK0pS7wRtB1xH30rqahBIut9QJDbDePyvtyqvH/Q=="],
|
||||
|
||||
"@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/resources": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-BeJLtU+f5Gf905cJX9vXFQorAr6TAfK3SPvTFqP+scfIpDQEJfRaGJWta7sJgP+m4dNtBf9y3yvBKVAZZtJQVA=="],
|
||||
"@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/resources": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-RKMn3QKi8nE71ULUo0g/MBvq1N4icEBo7cQSKnL3URZT16/YH3nSVgWegOjwx7FRBTrjOIkMJkCUn/ZFIEfn4A=="],
|
||||
|
||||
"@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-logs-otlp-grpc": "0.200.0", "@opentelemetry/exporter-logs-otlp-http": "0.200.0", "@opentelemetry/exporter-logs-otlp-proto": "0.200.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.200.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.200.0", "@opentelemetry/exporter-prometheus": "0.200.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.200.0", "@opentelemetry/exporter-trace-otlp-http": "0.200.0", "@opentelemetry/exporter-trace-otlp-proto": "0.200.0", "@opentelemetry/exporter-zipkin": "2.0.0", "@opentelemetry/instrumentation": "0.200.0", "@opentelemetry/propagator-b3": "2.0.0", "@opentelemetry/propagator-jaeger": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "@opentelemetry/sdk-trace-node": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-S/YSy9GIswnhYoDor1RusNkmRughipvTCOQrlF1dzI70yQaf68qgf5WMnzUxdlCl3/et/pvaO75xfPfuEmCK5A=="],
|
||||
|
||||
"@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ=="],
|
||||
"@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/resources": "2.5.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw=="],
|
||||
|
||||
"@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.5.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.5.0", "@opentelemetry/core": "2.5.0", "@opentelemetry/sdk-trace-base": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-O6N/ejzburFm2C84aKNrwJVPpt6HSTSq8T0ZUMq3xT2XmqT4cwxUItcL5UWGThYuq8RTcbH8u1sfj6dmRci0Ow=="],
|
||||
"@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.5.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.5.1", "@opentelemetry/core": "2.5.1", "@opentelemetry/sdk-trace-base": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-9lopQ6ZoElETOEN0csgmtEV5/9C7BMfA7VtF4Jape3i954b6sTY2k3Xw3CxUTKreDck/vpAuJM+EDo4zheUw+A=="],
|
||||
|
||||
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.39.0", "", {}, "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg=="],
|
||||
|
||||
@ -289,7 +289,7 @@
|
||||
|
||||
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
|
||||
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
||||
|
||||
"@types/node": ["@types/node@25.2.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ=="],
|
||||
|
||||
@ -311,7 +311,7 @@
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
|
||||
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
||||
|
||||
"char-info": ["char-info@0.3.6", "", { "dependencies": { "node-interval-tree": "^1.3.3" } }, "sha512-JskE1gqij+E0bHxs7hZt4h1xG3mvFntZGPn+jFWqImn2y/g53ugy/4J7/thcOCriiZFcLDFph4w+A0xbe2NFbg=="],
|
||||
|
||||
@ -333,7 +333,7 @@
|
||||
|
||||
"drizzle-orm": ["drizzle-orm@0.44.7", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ=="],
|
||||
|
||||
"elysia": ["elysia@1.4.23", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-mFIT/hEnNfrfbjGRUqunLNcURJfSXpKY4j+EWr4vP6Eoulf7feqs0WQLZwlgFZCxhdyfu0mrypIZ4mNJcEVVlQ=="],
|
||||
"elysia": ["elysia@1.4.25", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-liKjavH99Gpzrv9cDil6uYWmPuqESfPFV1FIaFSd3iNqo3y7e29sN43VxFIK8tWWnyi6eDAmi2SZk8hNAMQMyg=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
@ -485,19 +485,21 @@
|
||||
|
||||
"@logtape/otel/@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.208.0", "@opentelemetry/otlp-transformer": "0.208.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Wy8dZm16AOfM7yddEzSFzutHZDZ6HspKUODSUJVjyhnZFMBojWDjSNgduyCMlw6qaxJYz0dlb0OEcb4Eme+BfQ=="],
|
||||
|
||||
"@logtape/otel/@opentelemetry/resources": ["@opentelemetry/resources@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g=="],
|
||||
|
||||
"@logtape/otel/@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="],
|
||||
|
||||
"@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-prometheus/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="],
|
||||
|
||||
@ -505,11 +507,11 @@
|
||||
|
||||
"@opentelemetry/exporter-prometheus/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="],
|
||||
|
||||
"@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/exporter-zipkin/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="],
|
||||
|
||||
@ -523,7 +525,7 @@
|
||||
|
||||
"@opentelemetry/otlp-exporter-base/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="],
|
||||
|
||||
"@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.211.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/otlp-transformer": "0.211.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg=="],
|
||||
"@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.212.0", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/otlp-transformer": "0.212.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HoMv5pQlzbuxiMS0hN7oiUtg8RsJR5T7EhZccumIWxYfNo/f4wFc7LPDfFK6oHdG2JF/+qTocfqIHoom+7kLpw=="],
|
||||
|
||||
"@opentelemetry/propagator-b3/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="],
|
||||
|
||||
@ -625,6 +627,8 @@
|
||||
|
||||
"@logtape/otel/@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||
|
||||
"@logtape/otel/@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@2.5.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ=="],
|
||||
|
||||
"@logtape/otel/@opentelemetry/sdk-logs/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
|
||||
|
||||
"@logtape/otel/@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
|
||||
|
||||
@ -17,28 +17,28 @@
|
||||
"@logtape/otel": "2.0.2",
|
||||
"@logtape/redaction": "2.0.2",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/api-logs": "^0.211.0",
|
||||
"@opentelemetry/core": "^2.5.0",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.211.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.211.0",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.211.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.211.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.211.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.211.0",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.211.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.211.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.211.0",
|
||||
"@opentelemetry/resources": "^2.5.0",
|
||||
"@opentelemetry/sdk-logs": "^0.211.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.5.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.5.0",
|
||||
"@opentelemetry/sdk-trace-node": "^2.5.0",
|
||||
"@opentelemetry/api-logs": "^0.212.0",
|
||||
"@opentelemetry/core": "^2.5.1",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.212.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.212.0",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.212.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.212.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.212.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.212.0",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.212.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.212.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.212.0",
|
||||
"@opentelemetry/resources": "^2.5.1",
|
||||
"@opentelemetry/sdk-logs": "^0.212.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.5.1",
|
||||
"@opentelemetry/sdk-trace-base": "^2.5.1",
|
||||
"@opentelemetry/sdk-trace-node": "^2.5.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.39.0",
|
||||
"@types/bun": "^1.3.6",
|
||||
"@types/bun": "^1.3.9",
|
||||
"blurhash": "^2.0.5",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"drizzle-orm": "0.44.7",
|
||||
"elysia": "^1.4.23",
|
||||
"elysia": "^1.4.25",
|
||||
"jose": "^6.1.3",
|
||||
"node-addon-api": "^8.5.0",
|
||||
"parjs": "^1.3.9",
|
||||
@ -46,7 +46,7 @@
|
||||
"sharp": "^0.34.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.3.14",
|
||||
"@biomejs/biome": "2.4.0",
|
||||
"@types/pg": "^8.16.0"
|
||||
},
|
||||
"module": "src/index.js",
|
||||
|
||||
@ -135,7 +135,7 @@ const newsSort: Sort = {
|
||||
],
|
||||
};
|
||||
|
||||
const entryRelations = {
|
||||
export const entryRelations = {
|
||||
translations: () => {
|
||||
const { pk, language, ...trans } = getColumns(entryTranslations);
|
||||
return db
|
||||
|
||||
@ -6,6 +6,7 @@ import { entries } from "~/db/schema";
|
||||
import { watchlist } from "~/db/schema/watchlist";
|
||||
import { getColumns } from "~/db/utils";
|
||||
import { Entry } from "~/models/entry";
|
||||
import { Show } from "~/models/show";
|
||||
import {
|
||||
AcceptLanguage,
|
||||
createPage,
|
||||
@ -21,6 +22,7 @@ import { desc } from "~/models/utils/descriptions";
|
||||
import {
|
||||
entryFilters,
|
||||
entryProgressQ,
|
||||
entryRelations,
|
||||
entryVideosQ,
|
||||
getEntryTransQ,
|
||||
mapProgress,
|
||||
@ -71,7 +73,7 @@ export const nextup = new Elysia({ tags: ["profiles"] })
|
||||
query: { sort, filter, query, limit, after },
|
||||
headers: { "accept-language": languages, ...headers },
|
||||
request: { url },
|
||||
jwt: { sub },
|
||||
jwt: { sub, settings },
|
||||
}) => {
|
||||
const langs = processLanguages(languages);
|
||||
const transQ = getEntryTransQ(langs);
|
||||
@ -102,6 +104,11 @@ export const nextup = new Elysia({ tags: ["profiles"] })
|
||||
seasonNumber: sql<number>`${seasonNumber}`,
|
||||
episodeNumber: sql<number>`${episodeNumber}`,
|
||||
name: sql<string>`${transQ.name}`,
|
||||
|
||||
show: sql`${entryRelations.show({
|
||||
languages: langs,
|
||||
preferOriginal: settings.preferOriginal,
|
||||
})}`,
|
||||
})
|
||||
.from(entries)
|
||||
.innerJoin(watchlist, eq(watchlist.nextEntry, entries.pk))
|
||||
@ -134,7 +141,7 @@ export const nextup = new Elysia({ tags: ["profiles"] })
|
||||
"accept-language": AcceptLanguage({ autoFallback: true }),
|
||||
}),
|
||||
response: {
|
||||
200: Page(Entry),
|
||||
200: Page(t.Intersect([Entry, t.Object({ show: Show })])),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
{
|
||||
"extends": "//",
|
||||
"files": {
|
||||
"includes": [
|
||||
"src/**",
|
||||
"scripts/**"
|
||||
]
|
||||
"includes": ["**", "!public", "!.expo"]
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,8 +67,8 @@
|
||||
"zod": "^4.3.5",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.3.11",
|
||||
"@types/bun": "^1.3.6",
|
||||
"@biomejs/biome": "2.4.0",
|
||||
"@types/bun": "^1.3.9",
|
||||
"@types/react": "~19.1.10",
|
||||
"@types/react-dom": "~19.1.7",
|
||||
"typescript": "5.9.3",
|
||||
@ -270,23 +270,23 @@
|
||||
|
||||
"@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="],
|
||||
|
||||
"@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="],
|
||||
"@biomejs/biome": ["@biomejs/biome@2.4.0", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.0", "@biomejs/cli-darwin-x64": "2.4.0", "@biomejs/cli-linux-arm64": "2.4.0", "@biomejs/cli-linux-arm64-musl": "2.4.0", "@biomejs/cli-linux-x64": "2.4.0", "@biomejs/cli-linux-x64-musl": "2.4.0", "@biomejs/cli-win32-arm64": "2.4.0", "@biomejs/cli-win32-x64": "2.4.0" }, "bin": { "biome": "bin/biome" } }, "sha512-iluT61cORUDIC5i/y42ljyQraCemmmcgbMLLCnYO+yh+2hjTmcMFcwY8G0zTzWCsPb3t3AyKc+0t/VuhPZULUg=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="],
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-L+YpOtPSuU0etomfvFTPWRsa7+8ejaJL3yaROEoT/96HDJbR6OsvZQk0C8JUYou+XFdP+JcGxqZknkp4n934RA=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-fh7nnvbweDPm2xEmFjfmq7zSUiox88plgdHF9OIW4i99WnXrAC3o2P3ag9judoUMv8FCSUnlwJCM1B64nO5Fbg=="],
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Aq+S7ffpb5ynTyLgtnEjG+W6xuTd2F7FdC7J6ShpvRhZwJhjzwITGF9vrqoOnw0sv1XWkt2Q1Rpg+hleg/Xg7Q=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g=="],
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-u2p54IhvNAWB+h7+rxCZe3reNfQYFK+ppDw+q0yegrGclFYnDPZAntv/PqgUacpC3uxTeuWFgWW7RFe3lHuxOA=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg=="],
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-1rhDUq8sf7xX3tg7vbnU3WVfanKCKi40OXc4VleBMzRStmQHdeBY46aFP6VdwEomcVjyNiu+Zcr3LZtAdrZrjQ=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg=="],
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-WVFOhsnzhrbMGOSIcB9yFdRV2oG2KkRRhIZiunI9gJqSU3ax9ErdnTxRfJUxZUI9NbzVxC60OCXNcu+mXfF/Tw=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw=="],
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Omo0xhl63z47X+CrE5viEWKJhejJyndl577VoXg763U/aoATrK3r5+8DPh02GokWPeODX1Hek00OtjjooGan9w=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw=="],
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-aqRwW0LJLV1v1NzyLvLWQhdLmDSAV1vUh+OBdYJaa8f28XBn5BZavo+WTfqgEzALZxlNfBmu6NGO6Al3MbCULw=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="],
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.0", "", { "os": "win32", "cpu": "x64" }, "sha512-g47s+V+OqsGxbSZN3lpav6WYOk0PIc3aCBAq+p6dwSynL3K5MA6Cg6nkzDOlu28GEHwbakW+BllzHCJCxnfK5Q=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
|
||||
|
||||
@ -442,7 +442,7 @@
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@legendapp/list": ["@legendapp/list@github:zoriya/legend-list#c36ff94", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "*" } }, "zoriya-legend-list-c36ff94"],
|
||||
"@legendapp/list": ["@legendapp/list@github:zoriya/legend-list#a7465a6", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "*" } }, "zoriya-legend-list-a7465a6"],
|
||||
|
||||
"@material-symbols/svg-400": ["@material-symbols/svg-400@0.40.2", "", {}, "sha512-e2yEgZW/OveVT1sGaZW1kkRWTPVghjsJYWy+vIea3q08Fv2o7FCYv23PESMyr5D4AaAXdM5dKWkF1e6yIm4swA=="],
|
||||
|
||||
@ -532,7 +532,7 @@
|
||||
|
||||
"@react-native/virtualized-lists": ["@react-native/virtualized-lists@0.81.5", "", { "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@types/react": "^19.1.0", "react": "*", "react-native": "*" }, "optionalPeers": ["@types/react"] }, "sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw=="],
|
||||
|
||||
"@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.10.0", "", { "dependencies": { "@react-navigation/elements": "^2.9.5", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "@react-navigation/native": "^7.1.28", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-4YPB3cAtt5hwNnR3cpU4c85g1CXd8BJ9Eop1D/hls0zC2rAwbFrTk/jMCSxCvXJzDrYam0cgvcN+jk03jLmkog=="],
|
||||
"@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.13.0", "", { "dependencies": { "@react-navigation/elements": "^2.9.5", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "@react-navigation/native": "^7.1.28", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-qxxjRDpjhZ4vIZqG4rBU1Vx2jgOAO/ciUKc9sJqVlTM005E2E+aK1EaE3lGaBDyZxTpjonollAucZcqL7OTscQ=="],
|
||||
|
||||
"@react-navigation/core": ["@react-navigation/core@7.14.0", "", { "dependencies": { "@react-navigation/routers": "^7.5.3", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "query-string": "^7.1.3", "react-is": "^19.1.0", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": ">= 18.2.0" } }, "sha512-tMpzskBzVp0E7CRNdNtJIdXjk54Kwe/TF9ViXAef+YFM1kSfGv4e/B2ozfXE+YyYgmh4WavTv8fkdJz1CNyu+g=="],
|
||||
|
||||
@ -604,13 +604,13 @@
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="],
|
||||
|
||||
"@tanstack/query-core": ["@tanstack/query-core@5.90.18", "", {}, "sha512-rbGx6bHgPNVzutP7BEr+53UPKohpckqlMAad+To9UxTbeaQ+kC/1SDRj+QzkwbQ7qhLT/1IKp34yS6thda6fzA=="],
|
||||
"@tanstack/query-core": ["@tanstack/query-core@5.90.20", "", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="],
|
||||
|
||||
"@tanstack/query-devtools": ["@tanstack/query-devtools@5.92.0", "", {}, "sha512-N8D27KH1vEpVacvZgJL27xC6yPFUy0Zkezn5gnB3L3gRCxlDeSuiya7fKge8Y91uMTnC8aSxBQhcK6ocY7alpQ=="],
|
||||
"@tanstack/query-devtools": ["@tanstack/query-devtools@5.93.0", "", {}, "sha512-+kpsx1NQnOFTZsw6HAFCW3HkKg0+2cepGtAWXjiiSOJJ1CtQpt72EE2nyZb+AjAbLRPoeRmPJ8MtQd8r8gsPdg=="],
|
||||
|
||||
"@tanstack/react-query": ["@tanstack/react-query@5.90.18", "", { "dependencies": { "@tanstack/query-core": "5.90.18" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-KqNZX0C5IFz4639zR1ilnQ288tQdJrMNLtzmlzyJ14xauBkhtLEy3mPU/V4KiHsr41eL1ILZbDP36TB12lYfCQ=="],
|
||||
"@tanstack/react-query": ["@tanstack/react-query@5.90.21", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg=="],
|
||||
|
||||
"@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.91.2", "", { "dependencies": { "@tanstack/query-devtools": "5.92.0" }, "peerDependencies": { "@tanstack/react-query": "^5.90.14", "react": "^18 || ^19" } }, "sha512-ZJ1503ay5fFeEYFUdo7LMNFzZryi6B0Cacrgr2h1JRkvikK1khgIq6Nq2EcblqEdIlgB/r7XDW8f8DQ89RuUgg=="],
|
||||
"@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.91.3", "", { "dependencies": { "@tanstack/query-devtools": "5.93.0" }, "peerDependencies": { "@tanstack/react-query": "^5.90.20", "react": "^18 || ^19" } }, "sha512-nlahjMtd/J1h7IzOOfqeyDh5LNfG0eULwlltPEonYy0QL+nqrBB+nyzJfULV+moL7sZyxc2sHdNJki+vLA9BSA=="],
|
||||
|
||||
"@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="],
|
||||
|
||||
@ -622,7 +622,7 @@
|
||||
|
||||
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
|
||||
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
||||
|
||||
"@types/graceful-fs": ["@types/graceful-fs@4.1.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ=="],
|
||||
|
||||
@ -656,7 +656,7 @@
|
||||
|
||||
"@urql/exchange-retry": ["@urql/exchange-retry@1.3.2", "", { "dependencies": { "@urql/core": "^5.1.2", "wonka": "^6.3.2" } }, "sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg=="],
|
||||
|
||||
"@videojs/http-streaming": ["@videojs/http-streaming@3.17.2", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/vhs-utils": "^4.1.1", "aes-decrypter": "^4.0.2", "global": "^4.4.0", "m3u8-parser": "^7.2.0", "mpd-parser": "^1.3.1", "mux.js": "7.1.0", "video.js": "^7 || ^8" } }, "sha512-VBQ3W4wnKnVKb/limLdtSD2rAd5cmHN70xoMf4OmuDd0t2kfJX04G+sfw6u2j8oOm2BXYM9E1f4acHruqKnM1g=="],
|
||||
"@videojs/http-streaming": ["@videojs/http-streaming@3.17.4", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/vhs-utils": "^4.1.1", "aes-decrypter": "^4.0.2", "global": "^4.4.0", "m3u8-parser": "^7.2.0", "mpd-parser": "^1.3.1", "mux.js": "7.1.0", "video.js": "^7 || ^8" } }, "sha512-XAvdG2dolBuV2Fx8bu1kjmQ2D4TonGzZH68Pgv/O9xMSFWdZtITSMFismeQLEAtMmGwze8qNJp3RgV+jStrJqg=="],
|
||||
|
||||
"@videojs/vhs-utils": ["@videojs/vhs-utils@4.1.1", "", { "dependencies": { "@babel/runtime": "^7.12.5", "global": "^4.4.0" } }, "sha512-5iLX6sR2ownbv4Mtejw6Ax+naosGvoT9kY+gcuHzANyUZZ+4NpeNdKMUhb6ag0acYej1Y7cmr/F2+4PrggMiVA=="],
|
||||
|
||||
@ -752,7 +752,7 @@
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
||||
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
@ -1034,7 +1034,7 @@
|
||||
|
||||
"i18next": ["i18next@25.7.4", "", { "dependencies": { "@babel/runtime": "^7.28.4" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-hRkpEblXXcXSNbw8mBNq9042OEetgyB/ahc/X17uV/khPwzV+uB8RHceHh3qavyrkPJvmXFKXME2Sy1E0KjAfw=="],
|
||||
|
||||
"i18next-browser-languagedetector": ["i18next-browser-languagedetector@8.2.0", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g=="],
|
||||
"i18next-browser-languagedetector": ["i18next-browser-languagedetector@8.2.1", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw=="],
|
||||
|
||||
"i18next-http-backend": ["i18next-http-backend@3.0.2", "", { "dependencies": { "cross-fetch": "4.0.0" } }, "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g=="],
|
||||
|
||||
@ -1360,7 +1360,7 @@
|
||||
|
||||
"react-freeze": ["react-freeze@1.0.4", "", { "peerDependencies": { "react": ">=17.0.0" } }, "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA=="],
|
||||
|
||||
"react-i18next": ["react-i18next@16.5.3", "", { "dependencies": { "@babel/runtime": "^7.28.4", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 25.6.2", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-fo+/NNch37zqxOzlBYrWMx0uy/yInPkRfjSuy4lqKdaecR17nvCHnEUt3QyzA8XjQ2B/0iW/5BhaHR3ZmukpGw=="],
|
||||
"react-i18next": ["react-i18next@16.5.4", "", { "dependencies": { "@babel/runtime": "^7.28.4", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 25.6.2", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-6yj+dcfMncEC21QPhOTsW8mOSO+pzFmT6uvU7XXdvM/Cp38zJkmTeMeKmTrmCMD5ToT79FmiE/mRWiYWcJYW4g=="],
|
||||
|
||||
"react-is": ["react-is@19.2.3", "", {}, "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA=="],
|
||||
|
||||
@ -1372,9 +1372,9 @@
|
||||
|
||||
"react-native-localization-settings": ["react-native-localization-settings@1.2.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-pxX/mfokqjwIdb1zINuN6DLL4PeVHTaIGz2Tk833tS94fmpsSuPoYnkCmtXsfvZjxhDOSsRceao/JutJbIlpIQ=="],
|
||||
|
||||
"react-native-mmkv": ["react-native-mmkv@4.1.1", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }, "sha512-nYFjM27l7zVhIiyAqWEFRagGASecb13JMIlzAuOeakRRz9GMJ49hCQntUBE2aeuZRE4u4ehSqTOomB0mTF56Ew=="],
|
||||
"react-native-mmkv": ["react-native-mmkv@4.1.2", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }, "sha512-6LHb2DQBXuo96Aues13EugmlWw/HAYuh3KoJoQNrC4JsBwn3J3KiRYAg2mCm5Je0VYq2YsmbgZG7XJwX/WFYZA=="],
|
||||
|
||||
"react-native-nitro-modules": ["react-native-nitro-modules@0.33.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-ZlfOe6abODeHv/eZf8PxeSkrxIUhEKha6jaAAA9oXy7I6VPr7Ff4dUsAq3cyF3kX0L6qt2Dh9nzD2NdSsDwGpA=="],
|
||||
"react-native-nitro-modules": ["react-native-nitro-modules@0.33.9", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-BM9C5mCGYYjrc8CDWZZ0anLWU/knH2xaEuFzvzogKTOW6fzgS6mmsCdM3ty+AhImJNSYwK19DLrHaqwnrrwEzw=="],
|
||||
|
||||
"react-native-reanimated": ["react-native-reanimated@4.1.6", "", { "dependencies": { "react-native-is-edge-to-edge": "^1.2.1", "semver": "7.7.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*", "react-native-worklets": ">=0.5.0" } }, "sha512-F+ZJBYiok/6Jzp1re75F/9aLzkgoQCOh4yxrnwATa8392RvM3kx+fiXXFvwcgE59v48lMwd9q0nzF1oJLXpfxQ=="],
|
||||
|
||||
@ -1384,7 +1384,7 @@
|
||||
|
||||
"react-native-svg": ["react-native-svg@15.12.1", "", { "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g=="],
|
||||
|
||||
"react-native-svg-transformer": ["react-native-svg-transformer@1.5.2", "", { "dependencies": { "@svgr/core": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0", "@svgr/plugin-svgo": "^8.1.0", "path-dirname": "^1.0.2" }, "peerDependencies": { "react-native": ">=0.59.0", "react-native-svg": ">=12.0.0" } }, "sha512-eW4hOtrd30s4SRdN4X1XYxTCu1czsxDGQKmfQ3RFbZMN5yw4ZmiKGGr+lXbQW4uDGZvSoGd9FHL1f+rgGoKg8Q=="],
|
||||
"react-native-svg-transformer": ["react-native-svg-transformer@1.5.3", "", { "dependencies": { "@svgr/core": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0", "@svgr/plugin-svgo": "^8.1.0", "path-dirname": "^1.0.2" }, "peerDependencies": { "react-native": ">=0.59.0", "react-native-svg": ">=12.0.0" } }, "sha512-M4uFg5pUt35OMgjD4rWWbwd6PmxV96W7r/gQTTa+iZA5B+jO6aURhzAZGLHSrg1Kb91cKG0Rildy9q1WJvYstg=="],
|
||||
|
||||
"react-native-video": ["react-native-video@github:zoriya/react-native-video#b8ce1c9", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": ">=0.27.2" } }, "zoriya-react-native-video-b8ce1c9"],
|
||||
|
||||
@ -1530,9 +1530,9 @@
|
||||
|
||||
"svgo": ["svgo@3.3.2", "", { "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.0.0" }, "bin": "./bin/svgo" }, "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw=="],
|
||||
|
||||
"sweetalert2": ["sweetalert2@11.26.17", "", {}, "sha512-kkaySn1IRfwNlf9AkZVqDmBINDWw9NRR6Ij0O5dBRBOD1+mbtZJWxxR9/pA90nce9E5tIIkJ7SWij4rMWXtA1g=="],
|
||||
"sweetalert2": ["sweetalert2@11.26.18", "", {}, "sha512-3O5feBqV+hTIOwCRKGuZGHosjiuBAKP/vpBl6vKFZeVYfCUGdXqXuuidn6YXHan3f6e62UdmnjwJBt8UtDVBhg=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
|
||||
"tailwind-merge": ["tailwind-merge@3.4.1", "", {}, "sha512-2OA0rFqWOkITEAOFWSBSApYkDeH9t2B3XSJuI4YztKBzK3mX0737A2qtxDZ7xkw9Zfh0bWl+r34sF3HXV+Ig7Q=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
|
||||
|
||||
@ -1592,7 +1592,7 @@
|
||||
|
||||
"unique-string": ["unique-string@2.0.0", "", { "dependencies": { "crypto-random-string": "^2.0.0" } }, "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg=="],
|
||||
|
||||
"uniwind": ["uniwind@1.2.6", "", { "dependencies": { "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "culori": "4.0.2", "lightningcss": "1.30.2" }, "peerDependencies": { "react": ">=19.0.0", "react-native": ">=0.81.0", "tailwindcss": ">=4" } }, "sha512-DWzKsIzhI6fDmwtpewm71T6y8Wxq/P95VzmfThFearPuwyLeoLaR82OAAW2fXM75olRyE96zRo6IWydKMxErug=="],
|
||||
"uniwind": ["uniwind@1.3.1", "", { "dependencies": { "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "culori": "4.0.2", "lightningcss": "1.30.2" }, "peerDependencies": { "react": ">=19.0.0", "react-native": ">=0.81.0", "tailwindcss": ">=4" } }, "sha512-rXhalY2VN9jvZzLiotPltpq4SNsSXpy6CzwhqdeglmgVWIFRSYzIgEa+UnVbyjzPtwy9stfSHwQ4kbR5DmjhUA=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
@ -1616,7 +1616,7 @@
|
||||
|
||||
"vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="],
|
||||
|
||||
"video.js": ["video.js@8.23.4", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/http-streaming": "^3.17.2", "@videojs/vhs-utils": "^4.1.1", "@videojs/xhr": "2.7.0", "aes-decrypter": "^4.0.2", "global": "4.4.0", "m3u8-parser": "^7.2.0", "mpd-parser": "^1.3.1", "mux.js": "^7.0.1", "videojs-contrib-quality-levels": "4.1.0", "videojs-font": "4.2.0", "videojs-vtt.js": "0.15.5" } }, "sha512-qI0VTlYmKzEqRsz1Nppdfcaww4RSxZAq77z2oNSl3cNg2h6do5C8Ffl0KqWQ1OpD8desWXsCrde7tKJ9gGTEyQ=="],
|
||||
"video.js": ["video.js@8.23.7", "", { "dependencies": { "@babel/runtime": "^7.28.4", "@videojs/http-streaming": "^3.17.3", "@videojs/vhs-utils": "^4.1.1", "@videojs/xhr": "2.7.0", "aes-decrypter": "^4.0.2", "global": "4.4.0", "m3u8-parser": "^7.2.0", "mpd-parser": "^1.3.1", "mux.js": "^7.0.1", "videojs-contrib-quality-levels": "4.1.0", "videojs-font": "4.2.0", "videojs-vtt.js": "0.15.5" } }, "sha512-cG4HOygYt+Z8j6Sf5DuK6OgEOoM+g9oGP6vpqoZRaD13aHE4PMITbyjJUXZcIQbgB0wJEadBRaVm5lJIzo2jAA=="],
|
||||
|
||||
"videojs-contrib-quality-levels": ["videojs-contrib-quality-levels@4.1.0", "", { "dependencies": { "global": "^4.4.0" }, "peerDependencies": { "video.js": "^8" } }, "sha512-TfrXJJg1Bv4t6TOCMEVMwF/CoS8iENYsWNKip8zfhB5kTcegiFYezEA0eHAJPU64ZC8NQbxQgOwAsYU8VXbOWA=="],
|
||||
|
||||
@ -1674,7 +1674,7 @@
|
||||
|
||||
"yoshiki": ["yoshiki@1.2.14", "", { "dependencies": { "@types/inline-style-prefixer": "^5.0.0", "@types/node": "18.x.x", "@types/react": "18.x.x", "inline-style-prefixer": "^7.0.0" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native", "react-native-web"] }, "sha512-TQoaB1C8/rUCuz/856eDv1BUxN+/OYL0f7RG+MwqIv260BEQQeUrsGGaMfN2GHdy86geNkn9wQArJqsjut/3Lg=="],
|
||||
|
||||
"zod": ["zod@4.3.5", "", {}, "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g=="],
|
||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
|
||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
@ -1752,6 +1752,8 @@
|
||||
|
||||
"expo-router/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"expo-router/@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.10.0", "", { "dependencies": { "@react-navigation/elements": "^2.9.5", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "@react-navigation/native": "^7.1.28", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-4YPB3cAtt5hwNnR3cpU4c85g1CXd8BJ9Eop1D/hls0zC2rAwbFrTk/jMCSxCvXJzDrYam0cgvcN+jk03jLmkog=="],
|
||||
|
||||
"expo-router/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
|
||||
|
||||
"fbjs/cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="],
|
||||
|
||||
@ -77,8 +77,8 @@
|
||||
"zod": "^4.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.3.11",
|
||||
"@types/bun": "^1.3.6",
|
||||
"@biomejs/biome": "2.4.0",
|
||||
"@types/bun": "^1.3.9",
|
||||
"@types/react": "~19.1.10",
|
||||
"@types/react-dom": "~19.1.7",
|
||||
"typescript": "5.9.3"
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type { QueryPage } from "@kyoo/models";
|
||||
import { ts } from "@kyoo/primitives";
|
||||
import { ScrollView } from "react-native";
|
||||
import { DefaultLayout } from "../layout";
|
||||
import { Scanner } from "./scanner";
|
||||
import { UserList } from "./users";
|
||||
|
||||
export const AdminPage: QueryPage = () => {
|
||||
return (
|
||||
<ScrollView contentContainerStyle={{ gap: ts(4), paddingBottom: ts(4) }}>
|
||||
<UserList />
|
||||
<Scanner />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
AdminPage.getLayout = DefaultLayout;
|
||||
AdminPage.requiredPermissions = ["admin.read"];
|
||||
|
||||
AdminPage.getFetchUrls = () => [UserList.query()];
|
||||
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
type Issue,
|
||||
IssueP,
|
||||
type QueryIdentifier,
|
||||
queryFn,
|
||||
useFetch,
|
||||
} from "@kyoo/models";
|
||||
import { Button, Icon, P, Skeleton, tooltip, ts } from "@kyoo/primitives";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { useYoshiki } from "yoshiki/native";
|
||||
import { z } from "zod";
|
||||
import { SettingsContainer } from "../settings/base";
|
||||
|
||||
import Info from "@material-symbols/svg-400/outlined/info.svg";
|
||||
import Scan from "@material-symbols/svg-400/outlined/sensors.svg";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
export const Scanner = () => {
|
||||
const { css } = useYoshiki();
|
||||
const { t } = useTranslation();
|
||||
const { data, error } = useFetch(Scanner.query());
|
||||
|
||||
const metadataRefreshMutation = useMutation({
|
||||
mutationFn: () =>
|
||||
queryFn({
|
||||
path: ["rescan"],
|
||||
method: "POST",
|
||||
}),
|
||||
});
|
||||
|
||||
return (
|
||||
<SettingsContainer
|
||||
title={t("admin.scanner.label")}
|
||||
extraTop={
|
||||
<Button
|
||||
licon={<Icon icon={Scan} {...css({ marginX: ts(1) })} />}
|
||||
text={t("admin.scanner.scan")}
|
||||
onPress={() => metadataRefreshMutation.mutate()}
|
||||
{...css({ marginBottom: ts(2) })}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<>
|
||||
( (data ?? [...Array(3)])?.map((x, i) => (
|
||||
<View
|
||||
key={x?.cause ?? `${i}`}
|
||||
{...css({
|
||||
marginY: ts(1),
|
||||
marginX: ts(3),
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexGrow: 1,
|
||||
})}
|
||||
>
|
||||
<Icon
|
||||
icon={Info}
|
||||
{...css({ flexShrink: 0, marginRight: ts(2) })}
|
||||
{...tooltip(x?.cause)}
|
||||
/>
|
||||
<Skeleton>
|
||||
{x && (
|
||||
<P {...css({ flexGrow: 1, flexShrink: 1, flexWrap: "wrap" })}>
|
||||
{x.reason}
|
||||
</P>
|
||||
)}
|
||||
</Skeleton>
|
||||
</View>
|
||||
)) )}
|
||||
{data != null && data.length === 0 && <P>{t("admin.scanner.empty")}</P>}
|
||||
</>
|
||||
</SettingsContainer>
|
||||
);
|
||||
};
|
||||
|
||||
Scanner.query = (): QueryIdentifier<Issue[]> => ({
|
||||
parser: z.array(IssueP),
|
||||
path: ["issues"],
|
||||
params: {
|
||||
filter: "domain eq scanner",
|
||||
},
|
||||
});
|
||||
@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { type QueryIdentifier, type User, UserP, queryFn } from "@kyoo/models";
|
||||
import { Alert, Avatar, Icon, IconButton, Menu, P, Skeleton, tooltip, ts } from "@kyoo/primitives";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { px, useYoshiki } from "yoshiki/native";
|
||||
import type { Layout } from "../fetch";
|
||||
import { InfiniteFetch } from "../fetch-infinite";
|
||||
import { SettingsContainer } from "../settings/base";
|
||||
|
||||
import UserI from "@material-symbols/svg-400/rounded/account_circle.svg";
|
||||
import Delete from "@material-symbols/svg-400/rounded/delete.svg";
|
||||
import Unverifed from "@material-symbols/svg-400/rounded/gpp_bad.svg";
|
||||
import MoreVert from "@material-symbols/svg-400/rounded/more_vert.svg";
|
||||
import Admin from "@material-symbols/svg-400/rounded/shield_person.svg";
|
||||
import Verifed from "@material-symbols/svg-400/rounded/verified_user.svg";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
export const UserGrid = ({
|
||||
id,
|
||||
username,
|
||||
avatar,
|
||||
isAdmin,
|
||||
isVerified,
|
||||
...props
|
||||
}: {
|
||||
id: string;
|
||||
username: string;
|
||||
avatar: string;
|
||||
isAdmin: boolean;
|
||||
isVerified: boolean;
|
||||
}) => {
|
||||
const { css } = useYoshiki();
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const { mutateAsync } = useMutation({
|
||||
mutationFn: async (update: Partial<User>) =>
|
||||
await queryFn({
|
||||
path: ["users", id],
|
||||
method: "PATCH",
|
||||
body: update,
|
||||
}),
|
||||
onSettled: async () => await queryClient.invalidateQueries({ queryKey: ["users"] }),
|
||||
});
|
||||
|
||||
return (
|
||||
<View {...css({ alignItems: "center" }, props)}>
|
||||
<Avatar src={avatar} alt={username} placeholder={username} size={UserGrid.layout.size} fill />
|
||||
<View {...css({ flexDirection: "row", alignItems: "center" })}>
|
||||
<Icon
|
||||
icon={!isVerified ? Unverifed : isAdmin ? Admin : UserI}
|
||||
{...css({
|
||||
m: ts(1),
|
||||
})}
|
||||
{...tooltip(
|
||||
t(
|
||||
!isVerified
|
||||
? "admin.users.unverifed"
|
||||
: isAdmin
|
||||
? "admin.users.adminUser"
|
||||
: "admin.users.regularUser",
|
||||
),
|
||||
)}
|
||||
/>
|
||||
<P>{username}</P>
|
||||
<Menu Trigger={IconButton} icon={MoreVert} {...tooltip(t("misc.more"))}>
|
||||
{!isVerified && (
|
||||
<Menu.Item
|
||||
label={t("admin.users.verify")}
|
||||
icon={Verifed}
|
||||
onSelect={() =>
|
||||
mutateAsync({
|
||||
permissions: ["overall.read", "overall.play"],
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Menu.Sub label={t("admin.users.set-permissions")} icon={Admin}>
|
||||
<Menu.Item
|
||||
selected={!isAdmin}
|
||||
label={t("admin.users.regularUser")}
|
||||
onSelect={() =>
|
||||
mutateAsync({
|
||||
permissions: ["overall.read", "overall.play"],
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Menu.Item
|
||||
selected={isAdmin}
|
||||
label={t("admin.users.adminUser")}
|
||||
onSelect={() =>
|
||||
mutateAsync({
|
||||
permissions: [
|
||||
"overall.read",
|
||||
"overall.write",
|
||||
"overall.create",
|
||||
"overall.delete",
|
||||
"overall.play",
|
||||
"admin.read",
|
||||
"admin.write",
|
||||
"admin.create",
|
||||
"admin.delete",
|
||||
],
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Menu.Sub>
|
||||
<Menu.Item
|
||||
label={t("admin.users.delete")}
|
||||
icon={Delete}
|
||||
onSelect={async () => {
|
||||
Alert.alert(
|
||||
t("admin.users.delete"),
|
||||
t("login.delete-confirmation"),
|
||||
[
|
||||
{ text: t("misc.cancel"), style: "cancel" },
|
||||
{
|
||||
text: t("misc.delete"),
|
||||
onPress: async () => {
|
||||
await queryFn({ path: ["users", id], method: "DELETE" });
|
||||
await queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
},
|
||||
style: "destructive",
|
||||
},
|
||||
],
|
||||
{
|
||||
cancelable: true,
|
||||
icon: "warning",
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Menu>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
UserGrid.Loader = (props: object) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
<View {...css({ alignItems: "center" }, props)}>
|
||||
<Avatar.Loader size={UserGrid.layout.size} />
|
||||
<View {...css({ flexDirection: "row", alignItems: "center", flexShrink: 1, flexGrow: 1 })}>
|
||||
<Icon icon={UserI} {...css({ m: ts(1) })} />
|
||||
<Skeleton {...css({ flexGrow: 1, width: ts(8) })} />
|
||||
<IconButton icon={MoreVert} disabled />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
UserGrid.layout = {
|
||||
size: px(150),
|
||||
numColumns: { xs: 2, sm: 3, md: 5, lg: 6, xl: 7 },
|
||||
gap: { xs: ts(1), sm: ts(2), md: ts(4) },
|
||||
layout: "grid",
|
||||
} satisfies Layout;
|
||||
|
||||
export const UserList = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<SettingsContainer title={t("admin.users.label")}>
|
||||
<InfiniteFetch
|
||||
query={UserList.query()}
|
||||
layout={UserGrid.layout}
|
||||
Render={({ item }) => (
|
||||
<UserGrid
|
||||
id={item.id}
|
||||
username={item.username}
|
||||
avatar={item.logo}
|
||||
isAdmin={item.isAdmin}
|
||||
isVerified={item.isVerified}
|
||||
/>
|
||||
)}
|
||||
Loader={UserGrid.Loader}
|
||||
/>
|
||||
</SettingsContainer>
|
||||
);
|
||||
};
|
||||
|
||||
UserList.query = (): QueryIdentifier<User> => ({
|
||||
parser: UserP,
|
||||
path: ["users"],
|
||||
infinite: true,
|
||||
});
|
||||
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
type Collection,
|
||||
CollectionP,
|
||||
type LibraryItem,
|
||||
LibraryItemP,
|
||||
type QueryIdentifier,
|
||||
type QueryPage,
|
||||
getDisplayDate,
|
||||
} from "@kyoo/models";
|
||||
import {
|
||||
Container,
|
||||
GradientImageBackground,
|
||||
Head,
|
||||
P,
|
||||
Skeleton,
|
||||
ts,
|
||||
usePageStyle,
|
||||
} from "@kyoo/primitives";
|
||||
import { forwardRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Platform, View, type ViewProps } from "react-native";
|
||||
import { percent, px, useYoshiki } from "yoshiki/native";
|
||||
import { ItemGrid } from "../browse/grid";
|
||||
import { Header as ShowHeader, TitleLine } from "../../../../src/ui/details/headeri/details/header";
|
||||
import { SvgWave } from "../../../../src/ui/details/show/ui/details/show";
|
||||
import { Fetch } from "../fetch";
|
||||
import { InfiniteFetch } from "../fetch-infinite";
|
||||
import { ItemDetails } from "../home/recommended";
|
||||
import { DefaultLayout } from "../layout";
|
||||
|
||||
const Header = ({ slug }: { slug: string }) => {
|
||||
const { css } = useYoshiki();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Fetch query={Header.query(slug)}>
|
||||
{({ isLoading, ...data }) => (
|
||||
<>
|
||||
<Head title={data?.name} description={data?.overview} image={data?.thumbnail?.high} />
|
||||
|
||||
<GradientImageBackground
|
||||
src={data?.thumbnail}
|
||||
quality="high"
|
||||
alt=""
|
||||
containerStyle={ShowHeader.containerStyle}
|
||||
>
|
||||
<TitleLine
|
||||
isLoading={isLoading}
|
||||
type={"collection"}
|
||||
playHref={null}
|
||||
name={data?.name}
|
||||
tagline={null}
|
||||
date={null}
|
||||
rating={null}
|
||||
runtime={null}
|
||||
poster={data?.poster}
|
||||
trailerUrl={null}
|
||||
studio={null}
|
||||
{...css(ShowHeader.childStyle)}
|
||||
/>
|
||||
</GradientImageBackground>
|
||||
|
||||
<Container
|
||||
{...css({
|
||||
paddingTop: ts(4),
|
||||
marginBottom: ts(4),
|
||||
})}
|
||||
>
|
||||
<Skeleton lines={4}>
|
||||
{isLoading || (
|
||||
<P {...css({ textAlign: "justify" })}>{data.overview ?? t("show.noOverview")}</P>
|
||||
)}
|
||||
</Skeleton>
|
||||
</Container>
|
||||
</>
|
||||
)}
|
||||
</Fetch>
|
||||
);
|
||||
};
|
||||
|
||||
Header.query = (slug: string): QueryIdentifier<Collection> => ({
|
||||
parser: CollectionP,
|
||||
path: ["collections", slug],
|
||||
});
|
||||
|
||||
const CollectionHeader = forwardRef<View, ViewProps & { slug: string }>(function ShowHeader(
|
||||
{ children, slug, ...props },
|
||||
ref,
|
||||
) {
|
||||
const { css, theme } = useYoshiki();
|
||||
|
||||
return (
|
||||
<View
|
||||
ref={ref}
|
||||
{...css(
|
||||
[
|
||||
{ bg: (theme) => theme.variant.background },
|
||||
Platform.OS === "web" && {
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
// @ts-ignore Web only property
|
||||
overflowY: "auto" as any,
|
||||
},
|
||||
],
|
||||
props,
|
||||
)}
|
||||
>
|
||||
<Header slug={slug} />
|
||||
<SvgWave fill={theme.background} {...css({ flexShrink: 0, flexGrow: 1, display: "flex" })} />
|
||||
<View {...css({ bg: theme.background, paddingTop: { xs: ts(8), md: 0 } })}>
|
||||
<View
|
||||
{...css({
|
||||
width: percent(100),
|
||||
maxWidth: { xs: percent(100), lg: px(1170) },
|
||||
alignSelf: "center",
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
const query = (slug: string): QueryIdentifier<LibraryItem> => ({
|
||||
parser: LibraryItemP,
|
||||
path: ["collections", slug, "items"],
|
||||
infinite: true,
|
||||
params: {
|
||||
fields: ["firstEpisode", "episodesCount", "watchStatus"],
|
||||
},
|
||||
});
|
||||
|
||||
export const CollectionPage: QueryPage<{ slug: string }> = ({ slug }) => {
|
||||
const { css } = useYoshiki();
|
||||
const pageStyle = usePageStyle();
|
||||
|
||||
return (
|
||||
<InfiniteFetch
|
||||
query={query(slug)}
|
||||
placeholderCount={2}
|
||||
layout={{ ...ItemDetails.layout, numColumns: { xs: 1, md: 2 } }}
|
||||
Header={CollectionHeader}
|
||||
headerProps={{ slug }}
|
||||
contentContainerStyle={{ padding: 0, paddingHorizontal: 0, ...pageStyle }}
|
||||
Render={({ item }) => (
|
||||
<ItemDetails
|
||||
slug={item.slug}
|
||||
type={item.kind}
|
||||
name={item.name}
|
||||
tagline={"tagline" in item ? item.tagline : null}
|
||||
overview={item.overview}
|
||||
poster={item.poster}
|
||||
subtitle={item.kind !== "collection" ? getDisplayDate(item) : null}
|
||||
genres={"genres" in item ? item.genres : null}
|
||||
href={item.href}
|
||||
playHref={item.kind !== "collection" ? item.playHref : null}
|
||||
watchStatus={(item.kind !== "collection" && item.watchStatus?.status) || null}
|
||||
unseenEpisodesCount={
|
||||
item.kind === "show"
|
||||
? (item.watchStatus?.unseenEpisodesCount ?? item.episodesCount!)
|
||||
: null
|
||||
}
|
||||
{...css({ marginX: ItemGrid.layout.gap })}
|
||||
/>
|
||||
)}
|
||||
Loader={ItemDetails.Loader}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
CollectionPage.getLayout = { Layout: DefaultLayout, props: { transparent: true } };
|
||||
|
||||
CollectionPage.getFetchUrls = ({ slug }) => [query(slug), Header.query(slug)];
|
||||
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export { DownloadPage } from "./page";
|
||||
export { DownloadProvider, useDownloader } from "./state";
|
||||
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { type WatchInfo, getCurrentApiUrl, queryFn, toQueryKey } from "@kyoo/models";
|
||||
import { getCurrentAccount } from "@kyoo/models/src/account-internal";
|
||||
import type { ReactNode } from "react";
|
||||
import { Player } from "../../../../src/ui/player../src/ui/player";
|
||||
|
||||
export const useDownloader = () => {
|
||||
return async (type: "episode" | "movie", slug: string) => {
|
||||
const account = getCurrentAccount();
|
||||
const query = Player.infoQuery(type, slug);
|
||||
const info: WatchInfo = await queryFn(
|
||||
{
|
||||
queryKey: toQueryKey(query),
|
||||
signal: undefined as any,
|
||||
meta: undefined,
|
||||
apiUrl: account?.apiUrl,
|
||||
},
|
||||
query.parser,
|
||||
account?.token.access_token,
|
||||
);
|
||||
|
||||
// TODO: This methods does not work with auth.
|
||||
const a = document.createElement("a");
|
||||
a.style.display = "none";
|
||||
a.href = `${getCurrentApiUrl()!}/${type}/${slug}/direct`;
|
||||
a.download = `${slug}.${info.extension}`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
};
|
||||
};
|
||||
|
||||
export const DownloadPage = () => {};
|
||||
export const DownloadProvider = ({ children }: { children: ReactNode }) => children;
|
||||
@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type { KyooImage } from "@kyoo/models";
|
||||
import {
|
||||
Alert,
|
||||
H6,
|
||||
IconButton,
|
||||
ImageBackground,
|
||||
Menu,
|
||||
P,
|
||||
PressableFeedback,
|
||||
SubP,
|
||||
focusReset,
|
||||
ts,
|
||||
usePageStyle,
|
||||
} from "@kyoo/primitives";
|
||||
import DownloadForOffline from "@material-symbols/svg-400/rounded/download_for_offline.svg";
|
||||
import Downloading from "@material-symbols/svg-400/rounded/downloading.svg";
|
||||
import ErrorIcon from "@material-symbols/svg-400/rounded/error.svg";
|
||||
import NotStarted from "@material-symbols/svg-400/rounded/not_started.svg";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import { useRouter } from "expo-router";
|
||||
import { type Atom, useAtomValue } from "jotai";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { percent, useYoshiki } from "yoshiki/native";
|
||||
import { EpisodeLine, displayRuntime, episodeDisplayNumber } from "../../../../src/ui/details/episode";
|
||||
import { EmptyView } from "../fetch";
|
||||
import { type State, downloadAtom } from "./state";
|
||||
|
||||
const DownloadedItem = ({
|
||||
name,
|
||||
statusAtom,
|
||||
runtime,
|
||||
kind,
|
||||
image,
|
||||
...props
|
||||
}: {
|
||||
name: string;
|
||||
statusAtom: Atom<State>;
|
||||
runtime: number | null;
|
||||
kind: "episode" | "movie";
|
||||
image: KyooImage | null;
|
||||
}) => {
|
||||
const { css } = useYoshiki();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { error, status, progress, pause, resume, remove, play, retry } = useAtomValue(statusAtom);
|
||||
|
||||
return (
|
||||
<PressableFeedback
|
||||
onPress={() => play?.(router)}
|
||||
{...css(
|
||||
{
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
fover: {
|
||||
self: focusReset,
|
||||
title: {
|
||||
textDecorationLine: "underline",
|
||||
},
|
||||
},
|
||||
},
|
||||
props,
|
||||
)}
|
||||
>
|
||||
<ImageBackground
|
||||
src={image}
|
||||
quality="low"
|
||||
alt=""
|
||||
layout={{
|
||||
width: percent(25),
|
||||
aspectRatio: kind === "episode" ? 16 / 9 : 2 / 3,
|
||||
}}
|
||||
{...css({ flexShrink: 0, m: ts(1) })}
|
||||
>
|
||||
{/* {(watchedPercent || watchedStatus === WatchStatusV.Completed) && ( */}
|
||||
{/* <> */}
|
||||
{/* <View */}
|
||||
{/* {...css({ */}
|
||||
{/* backgroundColor: (theme) => theme.overlay0, */}
|
||||
{/* width: percent(100), */}
|
||||
{/* height: ts(0.5), */}
|
||||
{/* position: "absolute", */}
|
||||
{/* bottom: 0, */}
|
||||
{/* })} */}
|
||||
{/* /> */}
|
||||
{/* <View */}
|
||||
{/* {...css({ */}
|
||||
{/* backgroundColor: (theme) => theme.accent, */}
|
||||
{/* width: percent(watchedPercent ?? 100), */}
|
||||
{/* height: ts(0.5), */}
|
||||
{/* position: "absolute", */}
|
||||
{/* bottom: 0, */}
|
||||
{/* })} */}
|
||||
{/* /> */}
|
||||
{/* </> */}
|
||||
{/* )} */}
|
||||
</ImageBackground>
|
||||
<View
|
||||
{...css({
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
})}
|
||||
>
|
||||
<View {...css({ flexGrow: 1, flexShrink: 1 })}>
|
||||
{/* biome-ignore lint/a11y/useValidAriaValues: use h6 for style only */}
|
||||
<H6 aria-level={undefined} {...css([{ flexShrink: 1 }, "title"])}>
|
||||
{name ?? t("show.episodeNoMetadata")}
|
||||
</H6>
|
||||
{status === "FAILED" && <P>{t("downloads.error", { error: error ?? "Unknow error" })}</P>}
|
||||
{runtime && status === "DONE" && <SubP>{displayRuntime(runtime)}</SubP>}
|
||||
{progress !== 100 && progress !== null && (
|
||||
<View {...css({ flexDirection: "row", alignItems: "center" })}>
|
||||
<SubP {...css({ flexShrink: 0 })}>{Math.round(progress)}%</SubP>
|
||||
<View
|
||||
{...css({
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
marginLeft: ts(0.5),
|
||||
height: ts(0.5),
|
||||
backgroundColor: (theme) => theme.user.overlay0,
|
||||
})}
|
||||
>
|
||||
<View
|
||||
{...css({
|
||||
backgroundColor: (theme) => theme.user.accent,
|
||||
width: percent(progress),
|
||||
height: percent(100),
|
||||
})}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<Menu Trigger={IconButton} icon={downloadIcon(status)}>
|
||||
{status === "FAILED" && (
|
||||
<Menu.Item label={t("downloads.retry")} onSelect={() => retry?.()} />
|
||||
)}
|
||||
{status === "DOWNLOADING" && (
|
||||
<Menu.Item label={t("downloads.pause")} onSelect={() => pause?.()} />
|
||||
)}
|
||||
{status === "PAUSED" && (
|
||||
<Menu.Item label={t("downloads.resume")} onSelect={() => resume?.()} />
|
||||
)}
|
||||
<Menu.Item
|
||||
label={t("downloads.delete")}
|
||||
onSelect={() => {
|
||||
Alert.alert(
|
||||
t("downloads.delete"),
|
||||
t("downloads.deleteMessage"),
|
||||
[
|
||||
{ text: t("misc.cancel"), style: "cancel" },
|
||||
{ text: t("misc.delete"), onPress: remove, style: "destructive" },
|
||||
],
|
||||
{
|
||||
icon: "error",
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Menu>
|
||||
</View>
|
||||
</PressableFeedback>
|
||||
);
|
||||
};
|
||||
|
||||
const downloadIcon = (status: State["status"]) => {
|
||||
switch (status) {
|
||||
case "DONE":
|
||||
return DownloadForOffline;
|
||||
case "DOWNLOADING":
|
||||
return Downloading;
|
||||
case "FAILED":
|
||||
return ErrorIcon;
|
||||
case "PENDING":
|
||||
case "PAUSED":
|
||||
case "STOPPED":
|
||||
return NotStarted;
|
||||
}
|
||||
};
|
||||
|
||||
export const DownloadPage = () => {
|
||||
const pageStyle = usePageStyle();
|
||||
const downloads = useAtomValue(downloadAtom);
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (downloads.length === 0) return <EmptyView message={t("downloads.empty")} />;
|
||||
|
||||
return (
|
||||
<FlashList
|
||||
data={downloads}
|
||||
getItemType={(item) => item.data.kind}
|
||||
renderItem={({ item }) => (
|
||||
<DownloadedItem
|
||||
name={
|
||||
item.data.kind === "episode"
|
||||
? `${episodeDisplayNumber(item.data)!}: ${item.data.name}`
|
||||
: item.data.name
|
||||
}
|
||||
statusAtom={item.state}
|
||||
runtime={item.data.runtime}
|
||||
kind={item.data.kind}
|
||||
image={item.data.kind === "episode" ? item.data.thumbnail : item.data.poster}
|
||||
/>
|
||||
)}
|
||||
estimatedItemSize={EpisodeLine.layout.size}
|
||||
keyExtractor={(x) => x.data.id}
|
||||
numColumns={1}
|
||||
contentContainerStyle={pageStyle}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,308 +0,0 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import RNBackgroundDownloader, {
|
||||
type DownloadTask,
|
||||
} from "@kesha-antonov/react-native-background-downloader";
|
||||
import {
|
||||
type Account,
|
||||
type Episode,
|
||||
EpisodeP,
|
||||
type Movie,
|
||||
MovieP,
|
||||
type QueryIdentifier,
|
||||
type WatchInfo,
|
||||
WatchInfoP,
|
||||
queryFn,
|
||||
toQueryKey,
|
||||
} from "@kyoo/models";
|
||||
import { getCurrentAccount, storage } from "@kyoo/models/src/account-internal";
|
||||
import { type QueryClient, useQueryClient } from "@tanstack/react-query";
|
||||
import { deleteAsync } from "expo-file-system";
|
||||
import type { useRouter } from "expo-router";
|
||||
import { type PrimitiveAtom, atom, useSetAtom, useStore } from "jotai";
|
||||
import { type ReactNode, useEffect } from "react";
|
||||
import { ToastAndroid } from "react-native";
|
||||
import { z } from "zod";
|
||||
import { Player } from "../../../../src/ui/player";
|
||||
|
||||
type Router = ReturnType<typeof useRouter>;
|
||||
|
||||
export type State = {
|
||||
status: "DOWNLOADING" | "PAUSED" | "DONE" | "FAILED" | "STOPPED" | "PENDING";
|
||||
progress: number | null;
|
||||
size: number;
|
||||
availableSize: number;
|
||||
error?: string;
|
||||
pause: (() => void) | null;
|
||||
resume: (() => void) | null;
|
||||
remove: () => void;
|
||||
play: (router: Router) => void;
|
||||
retry: (() => void) | null;
|
||||
};
|
||||
|
||||
export const downloadAtom = atom<
|
||||
{
|
||||
data: Episode | Movie;
|
||||
info: WatchInfo;
|
||||
path: string;
|
||||
state: PrimitiveAtom<State>;
|
||||
}[]
|
||||
>([]);
|
||||
|
||||
const query = <T,>(query: QueryIdentifier<T>, info: Account): Promise<T> =>
|
||||
queryFn(
|
||||
{
|
||||
queryKey: toQueryKey(query),
|
||||
signal: undefined as any,
|
||||
meta: undefined,
|
||||
apiUrl: info.apiUrl,
|
||||
// use current user and current apiUrl to download this meta.
|
||||
},
|
||||
query.parser,
|
||||
info.token.access_token,
|
||||
);
|
||||
|
||||
const setupDownloadTask = (
|
||||
state: { data: Episode | Movie; info: WatchInfo; path: string },
|
||||
task: DownloadTask,
|
||||
store: ReturnType<typeof useStore>,
|
||||
queryClient: QueryClient,
|
||||
stateAtom?: PrimitiveAtom<State>,
|
||||
) => {
|
||||
if (!stateAtom) stateAtom = atom({} as State);
|
||||
store.set(stateAtom, {
|
||||
status: task.state,
|
||||
progress: task.bytesTotal ? (task.bytesDownloaded / task.bytesTotal) * 100 : null,
|
||||
size: task.bytesTotal,
|
||||
availableSize: task.bytesDownloaded,
|
||||
pause: () => {
|
||||
task.pause();
|
||||
store.set(stateAtom!, (x) => ({ ...x, state: "PAUSED" }));
|
||||
},
|
||||
resume: () => {
|
||||
task.resume();
|
||||
store.set(stateAtom!, (x) => ({ ...x, state: "DOWNLOADING" }));
|
||||
},
|
||||
remove: () => {
|
||||
task.stop();
|
||||
store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== task.id));
|
||||
},
|
||||
play: () => {
|
||||
ToastAndroid.show("The file has not finished downloading", ToastAndroid.LONG);
|
||||
},
|
||||
retry: () => {
|
||||
const [newTask, path] = download(
|
||||
{
|
||||
type: state.data.kind,
|
||||
id: state.data.id,
|
||||
slug: state.data.slug,
|
||||
extension: state.info.extension,
|
||||
},
|
||||
getCurrentAccount()!,
|
||||
);
|
||||
setupDownloadTask({ ...state, path }, newTask, store, queryClient, stateAtom);
|
||||
},
|
||||
});
|
||||
|
||||
// we use the store instead of the onMount because we want to update the state to cache it even if it was not
|
||||
// used during this launch of the app.
|
||||
const update = updater(store, stateAtom);
|
||||
|
||||
task
|
||||
.begin(({ expectedBytes }) => update((x) => ({ ...x, size: expectedBytes })))
|
||||
.progress(({ bytesDownloaded, bytesTotal }) => {
|
||||
update((x) => ({
|
||||
...x,
|
||||
progress: Math.round((bytesDownloaded / bytesTotal) * 100),
|
||||
size: bytesTotal,
|
||||
availableSize: bytesDownloaded,
|
||||
status: "DOWNLOADING",
|
||||
}));
|
||||
})
|
||||
.done(() => {
|
||||
update((x) => ({ ...x, progress: 100, status: "DONE", play: playFn(state, queryClient) }));
|
||||
RNBackgroundDownloader.completeHandler(task.id);
|
||||
})
|
||||
.error(({ error }) => {
|
||||
update((x) => ({ ...x, status: "FAILED", error }));
|
||||
console.error(`Error downloading ${state.data.slug}`, error);
|
||||
ToastAndroid.show(`Error downloading ${state.data.slug}`, ToastAndroid.LONG);
|
||||
});
|
||||
|
||||
return { data: state.data, info: state.info, path: state.path, state: stateAtom };
|
||||
};
|
||||
|
||||
const updater = (
|
||||
store: ReturnType<typeof useStore>,
|
||||
atom: PrimitiveAtom<State>,
|
||||
): ((f: (old: State) => State) => void) => {
|
||||
return (f) => {
|
||||
// if it lags, we could only store progress info on status change and not on every change.
|
||||
store.set(atom, f);
|
||||
|
||||
const downloads = store.get(downloadAtom);
|
||||
storage.set(
|
||||
"downloads",
|
||||
JSON.stringify(
|
||||
downloads.map((d) => ({
|
||||
data: d.data,
|
||||
info: d.info,
|
||||
path: d.path,
|
||||
state: store.get(d.state),
|
||||
})),
|
||||
),
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const download = (
|
||||
{
|
||||
type,
|
||||
id,
|
||||
slug,
|
||||
extension,
|
||||
}: { slug: string; id: string; type: "episode" | "movie"; extension: string },
|
||||
account: Account,
|
||||
) => {
|
||||
// TODO: support custom paths
|
||||
const path = `${RNBackgroundDownloader.directories.documents}/${slug}-${id}.${extension}`;
|
||||
const task = RNBackgroundDownloader.download({
|
||||
id: id,
|
||||
// TODO: support variant qualities
|
||||
url: `${account.apiUrl}/${type}/${slug}/direct`,
|
||||
destination: path,
|
||||
headers: {
|
||||
Authorization: account.token.access_token,
|
||||
},
|
||||
isNotificationVisible: true,
|
||||
// TODO: Implement only wifi
|
||||
// network: Network.ALL,
|
||||
});
|
||||
console.log("Starting download", path);
|
||||
return [task, path] as const;
|
||||
};
|
||||
|
||||
export const useDownloader = () => {
|
||||
const setDownloads = useSetAtom(downloadAtom);
|
||||
const store = useStore();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return async (type: "episode" | "movie", slug: string) => {
|
||||
try {
|
||||
const account = getCurrentAccount()!;
|
||||
const [data, info] = await Promise.all([
|
||||
query(Player.query(type, slug), account),
|
||||
query(Player.infoQuery(type, slug), account),
|
||||
]);
|
||||
|
||||
if (store.get(downloadAtom).find((x) => x.data.id === data.id)) {
|
||||
ToastAndroid.show(`${slug} is already downloaded, skipping`, ToastAndroid.LONG);
|
||||
return;
|
||||
}
|
||||
|
||||
const [task, path] = download(
|
||||
{ type, slug, id: data.id, extension: info.extension },
|
||||
account,
|
||||
);
|
||||
setDownloads((x) => [
|
||||
...x,
|
||||
setupDownloadTask({ data, info, path }, task, store, queryClient),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.error("download error", e);
|
||||
ToastAndroid.show(`Error downloading ${slug}`, ToastAndroid.LONG);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const playFn =
|
||||
(dl: { data: Episode | Movie; info: WatchInfo; path: string }, queryClient: QueryClient) =>
|
||||
(router: Router) => {
|
||||
dl.data.links.direct = dl.path;
|
||||
dl.data.links.hls = null;
|
||||
queryClient.setQueryData(toQueryKey(Player.query(dl.data.kind, dl.data.slug)), dl.data);
|
||||
queryClient.setQueryData(toQueryKey(Player.infoQuery(dl.data.kind, dl.data.slug)), dl.info);
|
||||
router.push(
|
||||
dl.data.kind === "episode"
|
||||
? { pathname: "/watch/[slug]", params: { slug: dl.data.slug } }
|
||||
: { pathname: "/movie/[slug]/watch", params: { slug: dl.data.slug } },
|
||||
);
|
||||
};
|
||||
|
||||
export const DownloadProvider = ({ children }: { children: ReactNode }) => {
|
||||
const store = useStore();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
async function run() {
|
||||
if (store.get(downloadAtom).length) return;
|
||||
|
||||
const tasks = await RNBackgroundDownloader.checkForExistingDownloads();
|
||||
const dls: { data: Episode | Movie; info: WatchInfo; path: string; state: State }[] =
|
||||
JSON.parse(storage.getString("downloads") ?? "[]");
|
||||
const downloads = dls.map((dl) => {
|
||||
const t = tasks.find((x) => x.id === dl.data.id);
|
||||
if (t) return setupDownloadTask(dl, t, store, queryClient);
|
||||
|
||||
const stateAtom = atom({
|
||||
status: dl.state.status === "DONE" ? "DONE" : "FAILED",
|
||||
progress: dl.state.progress,
|
||||
size: dl.state.size,
|
||||
availableSize: dl.state.availableSize,
|
||||
pause: null,
|
||||
resume: null,
|
||||
play: playFn(dl, queryClient),
|
||||
remove: () => {
|
||||
deleteAsync(dl.path);
|
||||
store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== dl.data.id));
|
||||
},
|
||||
retry: () => {
|
||||
const [newTask, path] = download(
|
||||
{
|
||||
type: dl.data.kind,
|
||||
id: dl.data.id,
|
||||
slug: dl.data.slug,
|
||||
extension: dl.info.extension,
|
||||
},
|
||||
getCurrentAccount()!,
|
||||
);
|
||||
setupDownloadTask({ ...dl, path }, newTask, store, queryClient, stateAtom);
|
||||
},
|
||||
} as State);
|
||||
return {
|
||||
data: z.union([EpisodeP, MovieP]).parse(dl.data),
|
||||
info: WatchInfoP.parse(dl.info),
|
||||
path: dl.path,
|
||||
state: stateAtom,
|
||||
};
|
||||
});
|
||||
store.set(downloadAtom, downloads);
|
||||
|
||||
for (const t of tasks) {
|
||||
if (!downloads.find((x) => x.data.id === t.id)) t.stop();
|
||||
}
|
||||
RNBackgroundDownloader.ensureDownloadsAreRunning();
|
||||
}
|
||||
run();
|
||||
}, [store, queryClient]);
|
||||
|
||||
return children;
|
||||
};
|
||||
@ -1,16 +0,0 @@
|
||||
import intl from "langmap";
|
||||
|
||||
const seenNativeNames = new Set();
|
||||
|
||||
export const languageCodes = Object.keys(intl)
|
||||
.filter((x) => {
|
||||
const nativeName = intl[x]?.nativeName;
|
||||
|
||||
// Only include if nativeName is unique and defined
|
||||
if (nativeName && !seenNativeNames.has(nativeName)) {
|
||||
seenNativeNames.add(nativeName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.filter((x) => !x.includes("@"));
|
||||
@ -1,22 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"noEmit": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@ -1,14 +1,21 @@
|
||||
import Browse from "@material-symbols/svg-400/rounded/browse-fill.svg";
|
||||
// import Downloading from "@material-symbols/svg-400/rounded/downloading-fill.svg";
|
||||
import Home from "@material-symbols/svg-400/rounded/home-fill.svg";
|
||||
import { Tabs } from "expo-router";
|
||||
import { Slot, Tabs } from "expo-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Platform } from "react-native";
|
||||
import { Icon } from "~/primitives";
|
||||
import { cn } from "~/utils";
|
||||
|
||||
export const unstable_settings = {
|
||||
initialRouteName: "index",
|
||||
};
|
||||
|
||||
export default function TabsLayout() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (Platform.OS === "web") return <Slot />;
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { Stack } from "expo-router";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { useCSSVariable, useResolveClassNames } from "uniwind";
|
||||
import { NavbarRight, NavbarTitle } from "~/ui/navbar";
|
||||
import { NavbarLeft, NavbarRight } from "~/ui/navbar";
|
||||
|
||||
export { ErrorBoundary } from "~/ui/error-bondary";
|
||||
|
||||
export const unstable_settings = {
|
||||
initialRouteName: "(tabs)",
|
||||
};
|
||||
|
||||
export default function Layout() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const accent = useCSSVariable("--color-accent");
|
||||
@ -13,7 +17,7 @@ export default function Layout() {
|
||||
return (
|
||||
<Stack
|
||||
screenOptions={{
|
||||
headerTitle: () => <NavbarTitle />,
|
||||
headerTitle: () => <NavbarLeft />,
|
||||
headerRight: () => <NavbarRight />,
|
||||
contentStyle: {
|
||||
paddingLeft: insets.left,
|
||||
|
||||
@ -3,10 +3,16 @@ import { Slot } from "expo-router";
|
||||
import { Platform } from "react-native";
|
||||
import { Providers } from "~/providers";
|
||||
import "../global.css";
|
||||
import { Tooltip } from "~/primitives";
|
||||
import { Tooltip, useMobileHover } from "~/primitives";
|
||||
import "~/fonts.web.css";
|
||||
|
||||
export const unstable_settings = {
|
||||
initialRouteName: "(app)",
|
||||
};
|
||||
|
||||
export default function Layout() {
|
||||
useMobileHover();
|
||||
|
||||
return (
|
||||
<Providers>
|
||||
<Slot />
|
||||
|
||||
@ -14,7 +14,7 @@ export const Container = <AsProps = ViewProps>({
|
||||
return (
|
||||
<As
|
||||
className={cn(
|
||||
"flex w-full flex-1 self-center px-4",
|
||||
"flex w-full self-center px-4",
|
||||
"sm:w-xl md:w-3xl lg:w-5xl xl:w-7xl",
|
||||
className,
|
||||
)}
|
||||
|
||||
@ -5,11 +5,11 @@ import {
|
||||
Platform,
|
||||
Pressable,
|
||||
type PressableProps,
|
||||
Text,
|
||||
type TextProps,
|
||||
} from "react-native";
|
||||
import { useResolveClassNames } from "uniwind";
|
||||
import { cn } from "~/utils";
|
||||
import { P } from "./text";
|
||||
|
||||
export function useLinkTo({
|
||||
href,
|
||||
@ -57,7 +57,7 @@ export const A = ({
|
||||
const linkProps = useLinkTo({ href, replace });
|
||||
|
||||
return (
|
||||
<Text
|
||||
<P
|
||||
{...linkProps}
|
||||
className={cn(
|
||||
"select-text text-accent hover:underline focus:underline",
|
||||
@ -66,7 +66,7 @@ export const A = ({
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
</P>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import ExpandMore from "@material-symbols/svg-400/rounded/keyboard_arrow_down-fi
|
||||
import ExpandLess from "@material-symbols/svg-400/rounded/keyboard_arrow_up-fill.svg";
|
||||
import * as RSelect from "@radix-ui/react-select";
|
||||
import { forwardRef } from "react";
|
||||
import { View } from "react-native";
|
||||
import { Platform, View } from "react-native";
|
||||
import { cn } from "~/utils";
|
||||
import { Icon } from "./icons";
|
||||
import { PressableFeedback } from "./links";
|
||||
@ -27,7 +27,7 @@ export const Select = ({
|
||||
<RSelect.Root value={value} onValueChange={onValueChange}>
|
||||
<RSelect.Trigger aria-label={label} asChild>
|
||||
<InternalTriger
|
||||
Component={PressableFeedback}
|
||||
Component={Platform.OS === "web" ? "div" : PressableFeedback}
|
||||
className={cn(
|
||||
"group flex-row items-center justify-center overflow-hidden rounded-4xl",
|
||||
"border-2 border-accent p-1 outline-0 focus-within:bg-accent hover:bg-accent",
|
||||
|
||||
42
front/src/primitives/utils/hover.ts
Normal file
42
front/src/primitives/utils/hover.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { useEffect } from "react";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
let preventHover = false;
|
||||
let hoverTimeout: NodeJS.Timeout | number;
|
||||
|
||||
export const useMobileHover = () => {
|
||||
if (Platform.OS !== "web") return;
|
||||
|
||||
// biome-ignore lint/correctness/useHookAtTopLevel: const condition
|
||||
useEffect(() => {
|
||||
const enableHover = () => {
|
||||
console.log("pc");
|
||||
if (preventHover) return;
|
||||
document.body.classList.remove("noHover");
|
||||
};
|
||||
|
||||
const disableHover = () => {
|
||||
console.log("mobile");
|
||||
if (hoverTimeout) clearTimeout(hoverTimeout);
|
||||
preventHover = true;
|
||||
hoverTimeout = setTimeout(() => {
|
||||
preventHover = false;
|
||||
}, 1000);
|
||||
document.body.classList.add("noHover");
|
||||
};
|
||||
|
||||
document.addEventListener("touchstart", disableHover, true);
|
||||
document.addEventListener("mousemove", enableHover, true);
|
||||
return () => {
|
||||
document.removeEventListener("touchstart", disableHover);
|
||||
document.removeEventListener("mousemove", enableHover);
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useIsTouch = () => {
|
||||
if (Platform.OS !== "web") return true;
|
||||
if (typeof window === "undefined") return false;
|
||||
// TODO: Subscribe to the change.
|
||||
return document.body.classList.contains("noHover");
|
||||
};
|
||||
@ -1,6 +1,5 @@
|
||||
export * from "./breakpoint";
|
||||
export * from "./capitalize";
|
||||
export * from "./head";
|
||||
export * from "./nojs";
|
||||
export * from "./hover";
|
||||
export * from "./spacing";
|
||||
export * from "./touchonly";
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
import type { ViewProps } from "react-native";
|
||||
|
||||
export const hiddenIfNoJs: ViewProps = {
|
||||
style: { $$css: true, noJs: "noJsHidden" } as any,
|
||||
};
|
||||
|
||||
export const HiddenIfNoJs = () => (
|
||||
<noscript>
|
||||
<style>
|
||||
{`
|
||||
.noJsHidden {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</noscript>
|
||||
);
|
||||
@ -1,17 +1,3 @@
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export const important = <T,>(value: T): T => {
|
||||
return `${value} !important` as T;
|
||||
};
|
||||
|
||||
export const ts = (spacing: number) => {
|
||||
return spacing * 8;
|
||||
};
|
||||
|
||||
export const focusReset: object =
|
||||
Platform.OS === "web"
|
||||
? {
|
||||
boxShadow: "unset",
|
||||
outline: "none",
|
||||
}
|
||||
: {};
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
import { Platform, type ViewProps } from "react-native";
|
||||
|
||||
export const TouchOnlyCss = () => {
|
||||
return (
|
||||
<style jsx global>{`
|
||||
:where(body.noHover) .noTouch {
|
||||
display: none;
|
||||
}
|
||||
:where(body:not(.noHover)) .touchOnly {
|
||||
display: none;
|
||||
}
|
||||
`}</style>
|
||||
);
|
||||
};
|
||||
|
||||
export const touchOnly: ViewProps = {
|
||||
style:
|
||||
Platform.OS === "web"
|
||||
? ({ $$css: true, touchOnly: "touchOnly" } as any)
|
||||
: {},
|
||||
};
|
||||
export const noTouch: ViewProps = {
|
||||
style:
|
||||
Platform.OS === "web"
|
||||
? ({ $$css: true, noTouch: "noTouch" } as any)
|
||||
: { display: "none" },
|
||||
};
|
||||
|
||||
export const useIsTouch = () => {
|
||||
if (Platform.OS !== "web") return true;
|
||||
if (typeof window === "undefined") return false;
|
||||
// TODO: Subscribe to the change.
|
||||
return document.body.classList.contains("noHover");
|
||||
};
|
||||
@ -8,9 +8,9 @@ import { HeaderBackground, useScrollNavbar } from "../navbar";
|
||||
import { GenreGrid } from "./genre";
|
||||
import { Header } from "./header";
|
||||
import { NewsList } from "./news";
|
||||
import { NextupList } from "./nextup";
|
||||
import { Recommended } from "./recommended";
|
||||
import { VerticalRecommended } from "./vertical";
|
||||
import { WatchlistList } from "./watchlist";
|
||||
|
||||
export const HomePage = () => {
|
||||
const genres = shuffle(Object.values(Genre.enum));
|
||||
@ -50,7 +50,7 @@ export const HomePage = () => {
|
||||
)}
|
||||
Loader={Header.Loader}
|
||||
/>
|
||||
<WatchlistList />
|
||||
<NextupList />
|
||||
<NewsList />
|
||||
{genres
|
||||
.filter((_, i) => i < 2)
|
||||
@ -64,10 +64,11 @@ export const HomePage = () => {
|
||||
<GenreGrid key={x} genre={x} />
|
||||
))}
|
||||
<VerticalRecommended />
|
||||
{/*
|
||||
TODO: Lazy load those items
|
||||
{randomItems.filter((_, i) => i >= 6).map((x) => <GenreGrid key={x} genre={x} />)}
|
||||
*/}
|
||||
{genres
|
||||
.filter((_, i) => i >= 6)
|
||||
.map((x) => (
|
||||
<GenreGrid key={x} genre={x} />
|
||||
))}
|
||||
</Animated.ScrollView>
|
||||
</>
|
||||
);
|
||||
@ -75,7 +76,7 @@ export const HomePage = () => {
|
||||
|
||||
HomePage.queries = (randomItems: Genre[]) => [
|
||||
Header.query(),
|
||||
WatchlistList.query(),
|
||||
NextupList.query(),
|
||||
NewsList.query(),
|
||||
...randomItems.filter((_, i) => i < 6).map((x) => GenreGrid.query(x)),
|
||||
Recommended.query(),
|
||||
|
||||
@ -15,20 +15,18 @@ export const NewsList = () => {
|
||||
query={NewsList.query()}
|
||||
layout={{ ...EntryBox.layout, layout: "horizontal" }}
|
||||
Empty={<EmptyView message={t("home.none")} />}
|
||||
Render={({ item }) => {
|
||||
return (
|
||||
<EntryBox
|
||||
kind={item.kind}
|
||||
slug={item.slug}
|
||||
serieSlug={item.show!.slug}
|
||||
name={`${item.show!.name} ${entryDisplayNumber(item)}`}
|
||||
description={item.name}
|
||||
thumbnail={item.thumbnail}
|
||||
href={item.href ?? "#"}
|
||||
watchedPercent={item.progress.percent}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
Render={({ item }) => (
|
||||
<EntryBox
|
||||
kind={item.kind}
|
||||
slug={item.slug}
|
||||
serieSlug={item.show!.slug}
|
||||
name={`${item.show!.name} ${entryDisplayNumber(item)}`}
|
||||
description={item.name}
|
||||
thumbnail={item.thumbnail ?? item.show!.thumbnail}
|
||||
href={item.href ?? "#"}
|
||||
watchedPercent={item.progress.percent}
|
||||
/>
|
||||
)}
|
||||
Loader={EntryBox.Loader}
|
||||
/>
|
||||
</>
|
||||
|
||||
66
front/src/ui/home/nextup.tsx
Normal file
66
front/src/ui/home/nextup.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { EntryBox, entryDisplayNumber } from "~/components/entries";
|
||||
import { ItemGrid } from "~/components/items";
|
||||
import { Entry } from "~/models";
|
||||
import { Button, Link, P } from "~/primitives";
|
||||
import { useAccount } from "~/providers/account-context";
|
||||
import { InfiniteFetch, type QueryIdentifier } from "~/query";
|
||||
import { EmptyView } from "~/ui/empty-view";
|
||||
import { Header } from "./genre";
|
||||
|
||||
export const NextupList = () => {
|
||||
const { t } = useTranslation();
|
||||
const account = useAccount();
|
||||
|
||||
if (!account) {
|
||||
return (
|
||||
<>
|
||||
<Header title={t("home.watchlist")} />
|
||||
<View className="items-center justify-center">
|
||||
<P>{t("home.watchlistLogin")}</P>
|
||||
<Button
|
||||
as={Link}
|
||||
href={"/login"}
|
||||
text={t("login.login")}
|
||||
className="m-4 min-w-md"
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header title={t("home.watchlist")} />
|
||||
<InfiniteFetch
|
||||
query={NextupList.query()}
|
||||
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
|
||||
Empty={<EmptyView message={t("home.none")} />}
|
||||
Render={({ item }) => (
|
||||
<EntryBox
|
||||
kind={item.kind}
|
||||
slug={item.slug}
|
||||
serieSlug={item.show!.slug}
|
||||
name={`${item.show!.name} ${entryDisplayNumber(item)}`}
|
||||
description={item.name}
|
||||
thumbnail={item.thumbnail ?? item.show!.thumbnail}
|
||||
href={item.href ?? "#"}
|
||||
watchedPercent={item.progress.percent}
|
||||
/>
|
||||
)}
|
||||
Loader={EntryBox.Loader}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
NextupList.query = (): QueryIdentifier<Entry> => ({
|
||||
parser: Entry,
|
||||
infinite: true,
|
||||
path: ["api", "profiles", "me", "nextup"],
|
||||
params: {
|
||||
limit: 10,
|
||||
with: ["nextEntry"],
|
||||
},
|
||||
});
|
||||
@ -1,80 +0,0 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { EntryBox, entryDisplayNumber } from "~/components/entries";
|
||||
import { ItemGrid, itemMap } from "~/components/items";
|
||||
import { Show } from "~/models";
|
||||
import { Button, Link, P } from "~/primitives";
|
||||
import { useAccount } from "~/providers/account-context";
|
||||
import { InfiniteFetch, type QueryIdentifier } from "~/query";
|
||||
import { EmptyView } from "~/ui/empty-view";
|
||||
import { Header } from "./genre";
|
||||
|
||||
export const WatchlistList = () => {
|
||||
const { t } = useTranslation();
|
||||
const account = useAccount();
|
||||
|
||||
if (!account) {
|
||||
return (
|
||||
<>
|
||||
<Header title={t("home.watchlist")} />
|
||||
<View className="items-center justify-center">
|
||||
<P>{t("home.watchlistLogin")}</P>
|
||||
<Button
|
||||
as={Link}
|
||||
href={"/login"}
|
||||
text={t("login.login")}
|
||||
className="m-4 min-w-md"
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header title={t("home.watchlist")} />
|
||||
<InfiniteFetch
|
||||
query={WatchlistList.query()}
|
||||
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
|
||||
getItemType={(x, i) =>
|
||||
(x?.kind === "serie" && x.nextEntry) || (!x && i % 2)
|
||||
? "episode"
|
||||
: "item"
|
||||
}
|
||||
getItemSizeMult={(_, __, kind) => (kind === "episode" ? 2 : 1)}
|
||||
Empty={<EmptyView message={t("home.none")} />}
|
||||
Render={({ item }) => {
|
||||
const entry = item.kind === "serie" ? item.nextEntry : null;
|
||||
if (entry) {
|
||||
return (
|
||||
<EntryBox
|
||||
kind={entry.kind}
|
||||
slug={entry.slug}
|
||||
serieSlug={item.slug}
|
||||
name={`${item.name} ${entryDisplayNumber(entry)}`}
|
||||
description={entry.name}
|
||||
thumbnail={entry.thumbnail ?? item.thumbnail}
|
||||
href={entry.href ?? "#"}
|
||||
watchedPercent={entry.progress.percent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <ItemGrid {...itemMap(item)} horizontal />;
|
||||
}}
|
||||
Loader={({ index }) =>
|
||||
index % 2 ? <EntryBox.Loader /> : <ItemGrid.Loader horizontal />
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
WatchlistList.query = (): QueryIdentifier<Show> => ({
|
||||
parser: Show,
|
||||
infinite: true,
|
||||
path: ["api", "profiles", "me", "watchlist"],
|
||||
params: {
|
||||
limit: 10,
|
||||
with: ["nextEntry"],
|
||||
},
|
||||
});
|
||||
@ -45,6 +45,24 @@ import { useAccount, useAccounts } from "~/providers/account-context";
|
||||
import { logout } from "~/ui/login/logic";
|
||||
import { cn } from "~/utils";
|
||||
|
||||
export const NavbarLeft = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (Platform.OS !== "web") return <NavbarTitle />;
|
||||
|
||||
return (
|
||||
<View className="flex-row items-center gap-2">
|
||||
<NavbarTitle />
|
||||
<A
|
||||
href="/browse"
|
||||
className="font-headers text-lg text-slate-200 uppercase dark:text-slate-200"
|
||||
>
|
||||
{t("navbar.browse")}
|
||||
</A>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavbarTitle = ({
|
||||
className,
|
||||
...props
|
||||
@ -64,68 +82,24 @@ export const NavbarTitle = ({
|
||||
);
|
||||
};
|
||||
|
||||
const getDisplayUrl = (url: string) => {
|
||||
url = url.replace(/\/api$/, "");
|
||||
url = url.replace(/https?:\/\//, "");
|
||||
return url;
|
||||
};
|
||||
|
||||
export const NavbarProfile = () => {
|
||||
export const NavbarRight = () => {
|
||||
const { t } = useTranslation();
|
||||
const account = useAccount();
|
||||
const accounts = useAccounts();
|
||||
const isAdmin = false; //useHasPermission(AdminPage.requiredPermissions);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
Trigger={Avatar<PressableProps>}
|
||||
as={PressableFeedback}
|
||||
src={account?.logo}
|
||||
placeholder={account?.username}
|
||||
alt={t("navbar.login")}
|
||||
className="m-2"
|
||||
{...tooltip(account?.username ?? t("navbar.login"))}
|
||||
>
|
||||
{accounts?.map((x) => (
|
||||
<Menu.Item
|
||||
key={x.id}
|
||||
label={
|
||||
Platform.OS === "web"
|
||||
? x.username
|
||||
: `${x.username} - ${getDisplayUrl(x.apiUrl)}`
|
||||
}
|
||||
left={
|
||||
<Avatar placeholder={x.username} src={x.logo} className="mx-2" />
|
||||
}
|
||||
selected={x.selected}
|
||||
onSelect={() => x.select()}
|
||||
<View className="shrink flex-row items-center">
|
||||
<SearchBar />
|
||||
{isAdmin && (
|
||||
<IconButton
|
||||
icon={Admin}
|
||||
as={Link}
|
||||
href={"/admin"}
|
||||
iconClassName="fill-slate-200 dark:fill-slate-200"
|
||||
{...tooltip(t("navbar.admin"))}
|
||||
/>
|
||||
))}
|
||||
{accounts.length > 0 && <HR />}
|
||||
<Menu.Item label={t("misc.settings")} icon={Settings} href="/settings" />
|
||||
{!account ? (
|
||||
<>
|
||||
<Menu.Item label={t("login.login")} icon={Login} href="/login" />
|
||||
<Menu.Item
|
||||
label={t("login.register")}
|
||||
icon={Register}
|
||||
href="/register"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Menu.Item
|
||||
label={t("login.add-account")}
|
||||
icon={Login}
|
||||
href="/login"
|
||||
/>
|
||||
<Menu.Item
|
||||
label={t("login.logout")}
|
||||
icon={Logout}
|
||||
onSelect={logout}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
<NavbarProfile />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@ -217,24 +191,68 @@ const SearchBar = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const NavbarRight = () => {
|
||||
const getDisplayUrl = (url: string) => {
|
||||
url = url.replace(/\/api$/, "");
|
||||
url = url.replace(/https?:\/\//, "");
|
||||
return url;
|
||||
};
|
||||
|
||||
export const NavbarProfile = () => {
|
||||
const { t } = useTranslation();
|
||||
const isAdmin = false; //useHasPermission(AdminPage.requiredPermissions);
|
||||
const account = useAccount();
|
||||
const accounts = useAccounts();
|
||||
|
||||
return (
|
||||
<View className="shrink flex-row items-center">
|
||||
<SearchBar />
|
||||
{isAdmin && (
|
||||
<IconButton
|
||||
icon={Admin}
|
||||
as={Link}
|
||||
href={"/admin"}
|
||||
iconClassName="fill-slate-200 dark:fill-slate-200"
|
||||
{...tooltip(t("navbar.admin"))}
|
||||
<Menu
|
||||
Trigger={Avatar<PressableProps>}
|
||||
as={PressableFeedback}
|
||||
src={account?.logo}
|
||||
placeholder={account?.username}
|
||||
alt={t("navbar.login")}
|
||||
className="m-2"
|
||||
{...tooltip(account?.username ?? t("navbar.login"))}
|
||||
>
|
||||
{accounts?.map((x) => (
|
||||
<Menu.Item
|
||||
key={x.id}
|
||||
label={
|
||||
Platform.OS === "web"
|
||||
? x.username
|
||||
: `${x.username} - ${getDisplayUrl(x.apiUrl)}`
|
||||
}
|
||||
left={
|
||||
<Avatar placeholder={x.username} src={x.logo} className="mx-2" />
|
||||
}
|
||||
selected={x.selected}
|
||||
onSelect={() => x.select()}
|
||||
/>
|
||||
))}
|
||||
{accounts.length > 0 && <HR />}
|
||||
<Menu.Item label={t("misc.settings")} icon={Settings} href="/settings" />
|
||||
{!account ? (
|
||||
<>
|
||||
<Menu.Item label={t("login.login")} icon={Login} href="/login" />
|
||||
<Menu.Item
|
||||
label={t("login.register")}
|
||||
icon={Register}
|
||||
href="/register"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Menu.Item
|
||||
label={t("login.add-account")}
|
||||
icon={Login}
|
||||
href="/login"
|
||||
/>
|
||||
<Menu.Item
|
||||
label={t("login.logout")}
|
||||
icon={Logout}
|
||||
onSelect={logout}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<NavbarProfile />
|
||||
</View>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -72,8 +72,6 @@ export const Controls = ({
|
||||
previous={previous}
|
||||
next={next}
|
||||
setMenu={setMenu}
|
||||
// Fixed is used because firefox android make the hover disappear under the navigation bar in absolute
|
||||
// position: Platform.OS === "web" ? ("fixed" as any) : "absolute",
|
||||
className="absolute bottom-0 w-full bg-slate-900/50 px-safe pt-safe"
|
||||
{...hoverControls}
|
||||
/>
|
||||
|
||||
@ -35,12 +35,12 @@ export const MiddleControls = ({
|
||||
"mx-6 bg-gray-800/70",
|
||||
!previous && "pointer-events-none opacity-0",
|
||||
)}
|
||||
iconClassName="h-16 w-16"
|
||||
iconClassName="h-16 w-16 fill-slate-200 dark:fill-slate-200"
|
||||
/>
|
||||
<PlayButton
|
||||
player={player}
|
||||
className={cn("mx-6 bg-gray-800/50")}
|
||||
iconClassName="h-24 w-24"
|
||||
iconClassName="h-24 w-24 fill-slate-200 dark:fill-slate-200"
|
||||
/>
|
||||
<IconButton
|
||||
icon={SkipNext}
|
||||
@ -51,7 +51,7 @@ export const MiddleControls = ({
|
||||
"mx-6 bg-gray-800/70",
|
||||
!next && "pointer-events-none opacity-0",
|
||||
)}
|
||||
iconClassName="h-16 w-16"
|
||||
iconClassName="h-16 w-16 fill-slate-200 dark:fill-slate-200"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
@ -2,9 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": [
|
||||
"./src/*"
|
||||
]
|
||||
"~/*": ["./src/*"]
|
||||
},
|
||||
"strict": true,
|
||||
"rootDir": ".",
|
||||
@ -17,21 +15,10 @@
|
||||
"jsx": "react-jsx",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"types": [
|
||||
"node",
|
||||
"react"
|
||||
],
|
||||
"lib": [
|
||||
"dom",
|
||||
"esnext"
|
||||
]
|
||||
"types": ["node", "react"],
|
||||
"lib": ["dom", "esnext"]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".expo/types/**/*.ts",
|
||||
"expo-env.d.ts"
|
||||
],
|
||||
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".expo",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user