Compare commits

...
Sign in to create a new pull request.

8 commits

13 changed files with 271 additions and 260 deletions

30
LICENCE Normal file
View file

@ -0,0 +1,30 @@
Copyright (c) 2021-2025, flashwave <me@flash.moe>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# Uiharu
Uiharu is a service that looks up metadata for a given URL.

View file

@ -1,6 +1,6 @@
{
"require": {
"flashwave/index": "^0.2410",
"flashwave/index": "^0.2503",
"sentry/sdk": "^4.0"
},
"autoload": {

289
composer.lock generated
View file

@ -4,25 +4,27 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "023574a93f076ef7a987151cdbfdb33e",
"content-hash": "ca6ae2c3d6229791acf93914e055bf41",
"packages": [
{
"name": "flashwave/index",
"version": "v0.2410.191603",
"version": "v0.2503.211818",
"source": {
"type": "git",
"url": "https://patchii.net/flash/index.git",
"reference": "17cdb4d1c239241200d7e30968122a8cd8b26509"
"reference": "8df1d32304eebab3f8d005634353c496679d5a87"
},
"require": {
"ext-mbstring": "*",
"php": ">=8.3",
"twig/html-extra": "^3.13",
"twig/twig": "^3.14"
"php": ">=8.4",
"psr/http-message": "^2.0",
"psr/http-server-handler": "^1.0",
"twig/html-extra": "^3.20",
"twig/twig": "^3.20"
},
"require-dev": {
"phpstan/phpstan": "^1.11",
"phpunit/phpunit": "^11.2"
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^12.0"
},
"suggest": {
"ext-memcache": "Support for the Index\\Cache\\Memcached namespace (only if you can't use ext-memcached for some reason).",
@ -59,7 +61,7 @@
],
"description": "Composer package for the common library for my projects.",
"homepage": "https://railgun.sh/index",
"time": "2024-10-19T16:04:17+00:00"
"time": "2025-03-21T18:19:04+00:00"
},
{
"name": "guzzlehttp/psr7",
@ -179,28 +181,29 @@
},
{
"name": "jean85/pretty-package-versions",
"version": "2.0.6",
"version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/Jean85/pretty-package-versions.git",
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4"
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4",
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0.0",
"php": "^7.1|^8.0"
"composer-runtime-api": "^2.1.0",
"php": "^7.4|^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"jean85/composer-provided-replaced-stub-package": "^1.0",
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^7.5|^8.5|^9.4",
"vimeo/psalm": "^4.3"
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^7.5|^8.5|^9.6",
"rector/rector": "^2.0",
"vimeo/psalm": "^4.3 || ^5.0"
},
"type": "library",
"extra": {
@ -232,9 +235,9 @@
],
"support": {
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6"
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
},
"time": "2024-03-08T09:58:59+00:00"
"time": "2025-03-19T14:43:43+00:00"
},
{
"name": "psr/http-factory",
@ -344,6 +347,62 @@
},
"time": "2023-04-04T09:54:51+00:00"
},
{
"name": "psr/http-server-handler",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-server-handler.git",
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4",
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4",
"shasum": ""
},
"require": {
"php": ">=7.0",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Server\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP server-side request handler",
"keywords": [
"handler",
"http",
"http-interop",
"psr",
"psr-15",
"psr-7",
"request",
"response",
"server"
],
"support": {
"source": "https://github.com/php-fig/http-server-handler/tree/1.0.2"
},
"time": "2023-04-10T20:06:20+00:00"
},
{
"name": "psr/log",
"version": "3.0.2",
@ -495,16 +554,16 @@
},
{
"name": "sentry/sentry",
"version": "4.9.0",
"version": "4.10.0",
"source": {
"type": "git",
"url": "https://github.com/getsentry/sentry-php.git",
"reference": "788ec170f51ebb22f2809a1e3f78b19ccd39b70d"
"reference": "2af937d47d8aadb8dab0b1d7b9557e495dd12856"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/788ec170f51ebb22f2809a1e3f78b19ccd39b70d",
"reference": "788ec170f51ebb22f2809a1e3f78b19ccd39b70d",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/2af937d47d8aadb8dab0b1d7b9557e495dd12856",
"reference": "2af937d47d8aadb8dab0b1d7b9557e495dd12856",
"shasum": ""
},
"require": {
@ -522,12 +581,12 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.4",
"guzzlehttp/promises": "^1.0|^2.0",
"guzzlehttp/promises": "^2.0.3",
"guzzlehttp/psr7": "^1.8.4|^2.1.1",
"monolog/monolog": "^1.6|^2.0|^3.0",
"phpbench/phpbench": "^1.0",
"phpstan/phpstan": "^1.3",
"phpunit/phpunit": "^8.5.14|^9.4",
"phpunit/phpunit": "^8.5|^9.6",
"symfony/phpunit-bridge": "^5.2|^6.0|^7.0",
"vimeo/psalm": "^4.17"
},
@ -568,7 +627,7 @@
],
"support": {
"issues": "https://github.com/getsentry/sentry-php/issues",
"source": "https://github.com/getsentry/sentry-php/tree/4.9.0"
"source": "https://github.com/getsentry/sentry-php/tree/4.10.0"
},
"funding": [
{
@ -580,20 +639,20 @@
"type": "custom"
}
],
"time": "2024-08-08T14:40:50+00:00"
"time": "2024-11-06T07:44:19+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.5.0",
"version": "v3.5.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"shasum": ""
},
"require": {
@ -601,12 +660,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@ -631,7 +690,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
},
"funding": [
{
@ -647,20 +706,20 @@
"type": "tidelift"
}
],
"time": "2024-04-18T09:32:20+00:00"
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/mime",
"version": "v7.1.5",
"version": "v7.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "711d2e167e8ce65b05aea6b258c449671cdd38ff"
"reference": "87ca22046b78c3feaff04b337f33b38510fd686b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/711d2e167e8ce65b05aea6b258c449671cdd38ff",
"reference": "711d2e167e8ce65b05aea6b258c449671cdd38ff",
"url": "https://api.github.com/repos/symfony/mime/zipball/87ca22046b78c3feaff04b337f33b38510fd686b",
"reference": "87ca22046b78c3feaff04b337f33b38510fd686b",
"shasum": ""
},
"require": {
@ -715,7 +774,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.1.5"
"source": "https://github.com/symfony/mime/tree/v7.2.4"
},
"funding": [
{
@ -731,20 +790,20 @@
"type": "tidelift"
}
],
"time": "2024-09-20T08:28:38+00:00"
"time": "2025-02-19T08:51:20+00:00"
},
{
"name": "symfony/options-resolver",
"version": "v7.1.1",
"version": "v7.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55"
"reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55",
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/7da8fbac9dcfef75ffc212235d76b2754ce0cf50",
"reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50",
"shasum": ""
},
"require": {
@ -782,7 +841,7 @@
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v7.1.1"
"source": "https://github.com/symfony/options-resolver/tree/v7.2.0"
},
"funding": [
{
@ -798,7 +857,7 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-11-20T11:17:29+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -826,8 +885,8 @@
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
@ -903,8 +962,8 @@
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
@ -985,8 +1044,8 @@
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
@ -1069,8 +1128,8 @@
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
@ -1123,98 +1182,22 @@
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php81",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php81\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "twig/html-extra",
"version": "v3.13.0",
"version": "v3.20.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/html-extra.git",
"reference": "8229e750091171c1f11801a525927811c7ac5a7e"
"reference": "f7d54d4de1b64182af745cfb66777f699b599734"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/html-extra/zipball/8229e750091171c1f11801a525927811c7ac5a7e",
"reference": "8229e750091171c1f11801a525927811c7ac5a7e",
"url": "https://api.github.com/repos/twigphp/html-extra/zipball/f7d54d4de1b64182af745cfb66777f699b599734",
"reference": "f7d54d4de1b64182af745cfb66777f699b599734",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"php": ">=8.1.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/mime": "^5.4|^6.4|^7.0",
"twig/twig": "^3.13|^4.0"
@ -1253,7 +1236,7 @@
"twig"
],
"support": {
"source": "https://github.com/twigphp/html-extra/tree/v3.13.0"
"source": "https://github.com/twigphp/html-extra/tree/v3.20.0"
},
"funding": [
{
@ -1265,30 +1248,30 @@
"type": "tidelift"
}
],
"time": "2024-09-03T13:08:40+00:00"
"time": "2025-01-31T20:45:36+00:00"
},
{
"name": "twig/twig",
"version": "v3.14.0",
"version": "v3.20.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72"
"reference": "3468920399451a384bef53cf7996965f7cd40183"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183",
"reference": "3468920399451a384bef53cf7996965f7cd40183",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"php": ">=8.1.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php81": "^1.29"
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"phpstan/phpstan": "^2.0",
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
},
@ -1332,7 +1315,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.14.0"
"source": "https://github.com/twigphp/Twig/tree/v3.20.0"
},
"funding": [
{
@ -1344,16 +1327,16 @@
"type": "tidelift"
}
],
"time": "2024-09-09T17:55:12+00:00"
"time": "2025-02-13T08:34:43+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

18
package-lock.json generated
View file

@ -126,9 +126,9 @@
}
},
"node_modules/domutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^2.0.0",
@ -208,9 +208,9 @@
}
},
"node_modules/parse5": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz",
"integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==",
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
"integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
"license": "MIT",
"dependencies": {
"entities": "^4.5.0"
@ -251,9 +251,9 @@
"license": "MIT"
},
"node_modules/undici": {
"version": "6.20.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.20.1.tgz",
"integrity": "sha512-AjQF1QsmqfJys+LXfGTNum+qw4S88CojRInG/6t31W/1fk6G59s92bnAvGz5Cmur+kQv2SURXEvvudLmbrE8QA==",
"version": "6.21.2",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz",
"integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==",
"license": "MIT",
"engines": {
"node": ">=18.17"

View file

@ -18,7 +18,7 @@
</div>
</div>
<div class="uiharu-footer">
<a href="https://flash.moe" target="_blank" rel="noopener">flashwave</a> 2021-2024
<a href="https://flash.moe" target="_blank" rel="noopener">flashwave</a> 2021-2025
</div>
</div>
</div>

View file

@ -3,9 +3,9 @@ namespace Uiharu;
require_once __DIR__ . '/../uiharu.php';
$ctx->registerLookup(new \Uiharu\Lookup\EEPROMLookup('eeprom', 'eeprom.flashii.net', ['i.fii.moe', 'i.flashii.net']));
$ctx->registerLookup(new \Uiharu\Lookup\EEPROMLookup('eeprom', 'flashii.net', ['i.fii.moe', 'i.flashii.net']));
if(UIH_DEBUG)
$ctx->registerLookup(new \Uiharu\Lookup\EEPROMLookup('devrom', 'eeprom.edgii.net', ['i.edgii.net']));
$ctx->registerLookup(new \Uiharu\Lookup\EEPROMLookup('devrom', 'edgii.net', ['i.edgii.net']));
$ctx->registerLookup(new \Uiharu\Lookup\YouTubeLookup($cfg->scopeTo('google')));
$ctx->registerLookup(new \Uiharu\Lookup\NicoNicoLookup);

View file

@ -17,11 +17,15 @@ use Uiharu\Lookup\NicoNicoLookupResult;
use Index\MediaType;
use Index\Cache\CacheProvider;
use Index\Colour\{Colour,ColourRgb};
use Index\Http\Routing\{HttpGet,HttpPost,RouteHandlerTrait};
use Index\Http\{HttpRequest,HttpResponseBuilder};
use Index\Http\Routing\RouteHandlerCommon;
use Index\Http\Routing\AccessControl\AccessControl;
use Index\Http\Routing\Routes\ExactRoute;
use Index\Http\Streams\Stream;
use Index\Performance\Stopwatch;
final class v1_0 implements \Uiharu\IApi {
use RouteHandlerTrait;
use RouteHandlerCommon;
private UihContext $ctx;
private CacheProvider $cache;
@ -35,8 +39,9 @@ final class v1_0 implements \Uiharu\IApi {
return !str_starts_with($url, '/v');
}
#[HttpGet('/metadata/thumb/audio')]
public function getThumbAudio($response, $request) {
#[AccessControl]
#[ExactRoute('GET', '/metadata/thumb/audio')]
public function getThumbAudio(HttpResponseBuilder $response, HttpRequest $request) {
$targetUrl = (string)$request->getParam('url');
if(empty($targetUrl))
@ -53,11 +58,12 @@ final class v1_0 implements \Uiharu\IApi {
$response->setContentType('image/png');
$response->setCacheControl('public', 'max-age=31536000', 'immutable');
$response->setContent(FFMPEG::grabFirstAudioCover($parsedUrl));
$response->body = Stream::createStream(FFMPEG::grabFirstAudioCover($parsedUrl));
}
#[HttpGet('/metadata/thumb/video')]
public function getThumbVideo($response, $request) {
#[AccessControl]
#[ExactRoute('GET', '/metadata/thumb/video')]
public function getThumbVideo(HttpResponseBuilder $response, HttpRequest $request) {
$targetUrl = (string)$request->getParam('url');
if(empty($targetUrl))
@ -74,55 +80,37 @@ final class v1_0 implements \Uiharu\IApi {
$response->setContentType('image/png');
$response->setCacheControl('public', 'max-age=31536000', 'immutable');
$response->setContent(FFMPEG::grabFirstVideoFrame($parsedUrl));
$response->body = Stream::createStream(FFMPEG::grabFirstVideoFrame($parsedUrl));
}
#[HttpGet('/metadata')]
public function getMetadata($response, $request) {
if($request->getMethod() === 'HEAD') {
$response->setTypeJson();
return;
}
#[AccessControl]
#[ExactRoute('GET', '/metadata')]
public function getMetadata(HttpResponseBuilder $response, HttpRequest $request) {
return $this->handleMetadata(
$response, $request,
(string)$request->getParam('url')
);
}
#[HttpPost('/metadata')]
public function postMetadata($response, $request) {
if(!$request->isStringContent())
return 400;
#[AccessControl]
#[ExactRoute('POST', '/metadata')]
public function postMetadata(HttpResponseBuilder $response, HttpRequest $request) {
return $this->handleMetadata(
$response, $request,
(string)$request->getContent()
(string)$request->getBody()
);
}
#[HttpGet('/metadata/batch')]
public function getMetadataBatch($response, $request) {
if($request->getMethod() === 'HEAD') {
$response->setTypeJson();
return;
}
return $this->handleMetadataBatch(
$response, $request,
$request->getParam('url', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY)
);
#[AccessControl]
#[ExactRoute('GET', '/metadata/batch')]
public function getMetadataBatch(): int {
return 410;
}
#[HttpPost('/metadata/batch')]
public function postMetadataBatch($response, $request) {
if(!$request->isFormContent())
return 400;
return $this->handleMetadataBatch(
$response, $request,
$request->getContent()->getParam('url', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY)
);
#[AccessControl]
#[ExactRoute('POST', '/metadata/batch')]
public function postMetadataBatch(): int {
return 410;
}
private function metadataLookup(string $targetUrl) {
@ -172,11 +160,11 @@ final class v1_0 implements \Uiharu\IApi {
$resp->content_type = MediaTypeExts::toV1($result->getMediaType());
if($result->hasColour()) {
$colour = $result->getColour();
if($colour->getAlpha() < 1.0)
if($colour->alpha < 1.0)
$colour = new ColourRgb(
$colour->getRed(),
$colour->getGreen(),
$colour->getBlue()
$colour->red,
$colour->green,
$colour->blue,
);
$resp->color = (string)$colour;
@ -278,7 +266,7 @@ final class v1_0 implements \Uiharu\IApi {
}
$sw->stop();
$resp->took = $sw->getElapsedTime() / 1000;
$resp->took = $sw->elapsedTime / 1000;
$respJson = json_encode($resp);
$this->cache->set($cacheKey, $respJson, 10 * 60);
@ -290,14 +278,14 @@ final class v1_0 implements \Uiharu\IApi {
return $resp;
}
private function handleMetadata($response, $request, string $targetUrl) {
private function handleMetadata(HttpResponseBuilder $response, HttpRequest $request, string $targetUrl) {
$result = $this->metadataLookup($targetUrl);
if(is_int($result))
return $result;
if(is_object($result)) {
if(!empty($result->status))
$response->setStatusCode($result->status);
$response->statusCode = $result->status;
} elseif(is_string($result)) {
$response->setTypeJson();
}
@ -305,7 +293,7 @@ final class v1_0 implements \Uiharu\IApi {
return $result;
}
private function handleMetadataBatch($response, $request, array $urls) {
private function handleMetadataBatch(HttpResponseBuilder $response, HttpRequest $request, array $urls) {
$sw = Stopwatch::startNew();
if(count($urls) > 20)

View file

@ -61,7 +61,9 @@ final class EEPROMLookup implements \Uiharu\ILookup {
if(!isset($fileId))
throw new RuntimeException('Was unable to find EEPROM file id.');
if(!preg_match('#^([A-Za-z0-9-_]+)$#', $fileId))
$fileId = pathinfo($fileId, PATHINFO_FILENAME);
if(!is_string($fileId) || !preg_match('#^([A-Za-z0-9-_]+)$#', $fileId))
throw new RuntimeException('Invalid EEPROM file id format.');
$fileInfo = $this->rawLookup($fileId);

View file

@ -141,7 +141,7 @@ final class WebLookup implements \Uiharu\ILookup {
// and i'm not about to rewrite this whole fucking thing in nodejs
// also at this point Index should probably provide a wrapper for proc_open lol
$extract = proc_open(
sprintf('node %s/extract.mjs', UIH_ROOT),
sprintf('/opt/nodejs/bin/node %s/extract.mjs', UIH_ROOT),
[0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']],
$pipes
);

View file

@ -7,14 +7,14 @@ final class MediaTypeExts {
public static function toV1(MediaType $mediaType): array {
$parts = [
'string' => (string)$mediaType,
'type' => $mediaType->getCategory(),
'subtype' => $mediaType->getKind(),
'type' => $mediaType->category,
'subtype' => $mediaType->kind,
];
if(!empty($suffix = $mediaType->getSuffix()))
if(!empty($suffix = $mediaType->suffix))
$parts['suffix'] = $suffix;
if(!empty($params = $mediaType->getParams()))
if(!empty($params = $mediaType->params))
$parts['params'] = $params;
return $parts;

View file

@ -3,12 +3,15 @@ namespace Uiharu;
use Index\Cache\CacheProvider;
use Index\Config\Config;
use Index\Http\Routing\HttpRouter;
use Index\Http\HttpResponseBuilder;
use Index\Http\Routing\{Router,RouteHandler,RouteHandlerCommon};
use Index\Http\Routing\Filters\PrefixFilter;
use Index\Http\Routing\Routes\ExactRoute;
final class UihContext {
private CacheProvider $cache;
private Config $config;
private HttpRouter $router;
private Router $router;
private array $apis = [];
private array $lookups = [];
@ -21,52 +24,28 @@ final class UihContext {
return $this->cache;
}
public function getRouter(): HttpRouter {
public function getRouter(): Router {
return $this->router;
}
public function isOriginAllowed(string $origin): bool {
$origin = mb_strtolower(parse_url($origin, PHP_URL_HOST));
if($origin === $_SERVER['HTTP_HOST'])
return true;
$allowed = $this->config->getArray('cors:origins');
if(empty($allowed))
return true;
return in_array($origin, $allowed);
}
public function setupHttp(): void {
$this->router = new HttpRouter;
$this->router->use('/', function($response) {
$response->setPoweredBy('Uiharu');
});
$this->router = new Router(
accessControlHandler: new UiharuAccessControlHandler($this->config->getArray('cors:origins')),
);
$this->router->register(new class implements RouteHandler {
use RouteHandlerCommon;
$this->router->use('/', function($response, $request) {
$origin = $request->getHeaderLine('Origin');
if(!empty($origin)) {
if(!$this->isOriginAllowed($origin))
return 403;
$response->setHeader('Access-Control-Allow-Origin', $origin);
$response->setHeader('Vary', 'Origin');
#[PrefixFilter('/')]
public function filterPoweredBy(HttpResponseBuilder $response): void {
$response->setPoweredBy('Uiharu');
}
});
$this->router->use('/', function($response, $request) {
if($request->getMethod() === 'OPTIONS') {
$response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, POST');
return 204;
#[ExactRoute('GET', '/')]
public function getIndex(HttpResponseBuilder $response): void {
$response->accelRedirect('/index.html');
$response->setContentType('text/html; charset=utf-8');
}
});
$this->router->get('/', function($response) {
$response->accelRedirect('/index.html');
$response->setContentType('text/html; charset=utf-8');
});
}
public function dispatchHttp(): void {

View file

@ -0,0 +1,26 @@
<?php
namespace Uiharu;
use Index\Http\HttpUri;
use Index\Http\Routing\HandlerContext;
use Index\Http\Routing\Routes\RouteInfo;
use Index\Http\Routing\AccessControl\{AccessControl,SimpleAccessControlHandler};
class UiharuAccessControlHandler extends SimpleAccessControlHandler {
public function __construct(
private array $origins,
) {}
#[\Override]
public function checkAccess(
HandlerContext $context,
AccessControl $accessControl,
HttpUri $origin,
?RouteInfo $routeInfo = null,
): string|bool {
if(!in_array($origin->host, $this->origins))
return false;
return $accessControl->credentials ? (string)$origin : true;
}
}