diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b33e7d5662..034fbe0008 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -33,6 +33,7 @@ jobs: - 'server/**' - 'openapi/**' - 'web/**' + - 'i18n/**' machine-learning: - 'machine-learning/**' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 84cc8914dc..52e0ba7b07 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,7 @@ jobs: filters: | web: - 'web/**' + - 'i18n/**' - 'open-api/typescript-sdk/**' server: - 'server/**' @@ -255,12 +256,6 @@ jobs: with: submodules: 'recursive' - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_READ_USERNAME }} - password: ${{ secrets.DOCKERHUB_READ_TOKEN }} - - name: Production build if: ${{ !cancelled() }} run: docker compose -f e2e/docker-compose.yml build @@ -284,12 +279,6 @@ jobs: with: submodules: 'recursive' - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_READ_USERNAME }} - password: ${{ secrets.DOCKERHUB_READ_TOKEN }} - - name: Setup Node uses: actions/setup-node@v4 with: @@ -332,12 +321,6 @@ jobs: with: submodules: 'recursive' - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_READ_USERNAME }} - password: ${{ secrets.DOCKERHUB_READ_TOKEN }} - - name: Setup Node uses: actions/setup-node@v4 with: diff --git a/README.md b/README.md index 5c4b9c39ed..7ad539c4cd 100644 --- a/README.md +++ b/README.md @@ -17,24 +17,24 @@
+

- -Català -Español -Français -Italiano -日本語 -한국어 -Deutsch -Nederlands -Türkçe -中文 -Русский -Português Brasileiro -Svenska -العربية -Tiếng Việt - + Català + Español + Français + Italiano + 日本語 + 한국어 + Deutsch + Nederlands + Türkçe + 中文 + Русский + Português Brasileiro + Svenska + العربية + Tiếng Việt + ภาษาไทย

## Disclaimer diff --git a/cli/package-lock.json b/cli/package-lock.json index b3f9111df4..c6e8c15650 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -24,7 +24,7 @@ "@types/cli-progress": "^3.11.0", "@types/lodash-es": "^4.17.12", "@types/mock-fs": "^4.13.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "@vitest/coverage-v8": "^2.0.5", @@ -59,7 +59,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "typescript": "^5.3.3" } }, @@ -1378,9 +1378,9 @@ } }, "node_modules/@types/node": { - "version": "20.16.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", - "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "version": "20.16.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.13.tgz", + "integrity": "sha512-GjQ7im10B0labo8ZGXDGROUl9k0BNyDgzfGpb4g/cl+4yYDWVKcozANF4FGr4/p0O/rAkQClM6Wiwkije++1Tg==", "dev": true, "license": "MIT", "dependencies": { @@ -1394,17 +1394,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", + "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/type-utils": "8.10.0", + "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1428,16 +1428,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", + "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4" }, "engines": { @@ -1457,14 +1457,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", + "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1475,14 +1475,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", + "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/utils": "8.10.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1500,9 +1500,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", + "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", "dev": true, "license": "MIT", "engines": { @@ -1514,14 +1514,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", + "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1543,16 +1543,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", + "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1566,13 +1566,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", + "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/types": "8.10.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1584,9 +1584,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.2.tgz", - "integrity": "sha512-b7kHrFrs2urS0cOk5N10lttI8UdJ/yP3nB4JYTREvR5o18cR99yPpK4gK8oQgI42BVv0ILWYUSYB7AXkAUDc0g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.3.tgz", + "integrity": "sha512-2OJ3c7UPoFSmBZwqD2VEkUw6A/tzPF0LmW0ZZhhB8PFxuc+9IBG/FaSM+RLEenc7ljzFvGN+G0nGQoZnh7sy2A==", "dev": true, "license": "MIT", "dependencies": { @@ -1607,8 +1607,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.2", - "vitest": "2.1.2" + "@vitest/browser": "2.1.3", + "vitest": "2.1.3" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1617,14 +1617,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.2.tgz", - "integrity": "sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz", + "integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -1633,13 +1633,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.2.tgz", - "integrity": "sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz", + "integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "^2.1.0-beta.1", + "@vitest/spy": "2.1.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.11" }, @@ -1647,7 +1647,7 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/spy": "2.1.2", + "@vitest/spy": "2.1.3", "msw": "^2.3.5", "vite": "^5.0.0" }, @@ -1661,9 +1661,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.2.tgz", - "integrity": "sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", + "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1674,13 +1674,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.2.tgz", - "integrity": "sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz", + "integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.2", + "@vitest/utils": "2.1.3", "pathe": "^1.1.2" }, "funding": { @@ -1688,13 +1688,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.2.tgz", - "integrity": "sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz", + "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "magic-string": "^0.30.11", "pathe": "^1.1.2" }, @@ -1703,9 +1703,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.2.tgz", - "integrity": "sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz", + "integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1716,13 +1716,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.2.tgz", - "integrity": "sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz", + "integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -3150,9 +3150,9 @@ } }, "node_modules/mock-fs": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.3.0.tgz", - "integrity": "sha512-IMvz1X+RF7vf+ur7qUenXMR7/FSKSIqS3HqFHXcyNI7G0FbpFO8L5lfsUJhl+bhK1AiulVHWKUSxebWauPA+xQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.0.tgz", + "integrity": "sha512-3ROPnEMgBOkusBMYQUW2rnT3wZwsgfOKzJDLvx/TZ7FL1WmWvwSwn3j4aDR5fLDGtgcc1WF0Z1y0di7c9L4FKw==", "dev": true, "license": "MIT", "engines": { @@ -4181,9 +4181,9 @@ } }, "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz", + "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==", "dev": true, "license": "MIT", "dependencies": { @@ -4241,9 +4241,9 @@ } }, "node_modules/vite-node": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.2.tgz", - "integrity": "sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz", + "integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==", "dev": true, "license": "MIT", "dependencies": { @@ -4283,19 +4283,19 @@ } }, "node_modules/vitest": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.2.tgz", - "integrity": "sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz", + "integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.2", - "@vitest/mocker": "2.1.2", - "@vitest/pretty-format": "^2.1.2", - "@vitest/runner": "2.1.2", - "@vitest/snapshot": "2.1.2", - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/expect": "2.1.3", + "@vitest/mocker": "2.1.3", + "@vitest/pretty-format": "^2.1.3", + "@vitest/runner": "2.1.3", + "@vitest/snapshot": "2.1.3", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "debug": "^4.3.6", "magic-string": "^0.30.11", @@ -4306,7 +4306,7 @@ "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.2", + "vite-node": "2.1.3", "why-is-node-running": "^2.3.0" }, "bin": { @@ -4321,8 +4321,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.2", - "@vitest/ui": "2.1.2", + "@vitest/browser": "2.1.3", + "@vitest/ui": "2.1.3", "happy-dom": "*", "jsdom": "*" }, @@ -4507,9 +4507,9 @@ } }, "node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", "dev": true, "license": "ISC", "bin": { diff --git a/cli/package.json b/cli/package.json index db07d1c289..57cbc502ed 100644 --- a/cli/package.json +++ b/cli/package.json @@ -20,7 +20,7 @@ "@types/cli-progress": "^3.11.0", "@types/lodash-es": "^4.17.12", "@types/mock-fs": "^4.13.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "@vitest/coverage-v8": "^2.0.5", diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 552b4a8673..f2509e0d2f 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -66,6 +66,7 @@ services: - 24678:24678 volumes: - ../web:/usr/src/app + - ../i18n:/usr/src/i18n - ../open-api/:/usr/src/open-api/ - /usr/src/app/node_modules ulimits: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index b02b015780..8358097bf3 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -94,7 +94,7 @@ services: container_name: immich_prometheus ports: - 9090:9090 - image: prom/prometheus@sha256:f6639335d34a77d9d9db382b92eeb7fc00934be8eae81dbc03b31cfe90411a94 + image: prom/prometheus@sha256:378f4e03703557d1c6419e6caccf922f96e6d88a530f7431d66a4c4f4b1000fe volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus diff --git a/docs/docs/administration/repair-page.md b/docs/docs/administration/repair-page.md index f230c6d582..4246c7e39c 100644 --- a/docs/docs/administration/repair-page.md +++ b/docs/docs/administration/repair-page.md @@ -1,5 +1,9 @@ # Repair Page +:::warning +This feature is currently disabled and will be reworked in the near future. +::: + The repair page is designed to give information to the system administrator about files that are not tracked, or offline paths. ## Natural State diff --git a/docs/docs/administration/server-stats.md b/docs/docs/administration/server-stats.md index b77037e4ce..eb5f72a41d 100644 --- a/docs/docs/administration/server-stats.md +++ b/docs/docs/administration/server-stats.md @@ -7,7 +7,7 @@ If a storage quota has been defined for the user, the usage number will be displ ::: :::info External library -External library is not included in the storage quota. +External libraries are not included in the storage quota. ::: diff --git a/docs/docs/developer/directories.md b/docs/docs/developer/directories.md index 3ec483294a..409353e2c4 100644 --- a/docs/docs/developer/directories.md +++ b/docs/docs/developer/directories.md @@ -15,7 +15,7 @@ Our [GitHub Repository](https://github.com/immich-app/immich) is a [monorepo](ht | `design/` | Screenshots and logos for the README | | `docs/` | Source code for the [https://immich.app](https://immich.app) website | | `machine-learning/` | Source code for the `immich-machine-learning` docker image | -| `misc/release/` | Scripts for version pumps and draft releases | +| `misc/release/` | Scripts for version bumps and draft releases | | `mobile/` | Source code for the mobile app, both Android and iOS | | `server/` | Source code for the `immich-server` docker image | | `web/` | Source code for the `web` | diff --git a/docs/docs/features/hardware-transcoding.md b/docs/docs/features/hardware-transcoding.md index 756bb6823c..4f059281f3 100644 --- a/docs/docs/features/hardware-transcoding.md +++ b/docs/docs/features/hardware-transcoding.md @@ -62,10 +62,13 @@ For RKMPP to work: 1. If you do not already have it, download the latest [`hwaccel.transcoding.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`. 2. In the `docker-compose.yml` under `immich-server`, uncomment the `extends` section and change `cpu` to the appropriate backend. -- For VAAPI on WSL2, be sure to use `vaapi-wsl` rather than `vaapi` + Note: For VAAPI on WSL2, be sure to use `vaapi-wsl` rather than `vaapi` 3. Redeploy the `immich-server` container with these updated settings. 4. In the Admin page under `Video transcoding settings`, change the hardware acceleration setting to the appropriate option and save. + + Note: For Jasper Lake and Elkhart Lake CPUs, you will need to set the `Hardware Acceleration` -> `Constant quality mode` to `CQP` + 5. (Optional) Enable hardware decoding for optimal performance. #### Single Compose File diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index bb9b4d434c..e86199dc74 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -148,23 +148,24 @@ Redis (Sentinel) URL example JSON before encoding: ## Machine Learning -| Variable | Description | Default | Containers | -| :-------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-----------------------------------: | :--------------- | -| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning | -| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning | -| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning | -| `MACHINE_LEARNING_REQUEST_THREADS`\*1 | Thread count of the request thread pool (disabled if \<= 0) | number of CPU cores | machine learning | -| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning | -| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning | -| `MACHINE_LEARNING_WORKERS`\*2 | Number of worker processes to spawn | `1` | machine learning | -| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`\*3 | HTTP Keep-alive time in seconds | `2` | machine learning | -| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO image) | machine learning | -| `MACHINE_LEARNING_PRELOAD__CLIP` | Name of a CLIP model to be preloaded and kept in cache | | machine learning | -| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION` | Name of a facial recognition model to be preloaded and kept in cache | | machine learning | -| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning | -| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning | -| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning | -| `MACHINE_LEARNING_DEVICE_IDS`\*4 | Device IDs to use in multi-GPU environments | `0` | machine learning | +| Variable | Description | Default | Containers | +| :-------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-----------------------------: | :--------------- | +| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning | +| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning | +| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning | +| `MACHINE_LEARNING_REQUEST_THREADS`\*1 | Thread count of the request thread pool (disabled if \<= 0) | number of CPU cores | machine learning | +| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS` | Number of parallel model operations | `1` | machine learning | +| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS` | Number of threads for each model operation | `2` | machine learning | +| `MACHINE_LEARNING_WORKERS`\*2 | Number of worker processes to spawn | `1` | machine learning | +| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`\*3 | HTTP Keep-alive time in seconds | `2` | machine learning | +| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO) | machine learning | +| `MACHINE_LEARNING_PRELOAD__CLIP` | Name of a CLIP model to be preloaded and kept in cache | | machine learning | +| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION` | Name of a facial recognition model to be preloaded and kept in cache | | machine learning | +| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning | +| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning | +| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning | +| `MACHINE_LEARNING_DEVICE_IDS`\*4 | Device IDs to use in multi-GPU environments | `0` | machine learning | +| `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning | \*1: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones. diff --git a/docs/docs/overview/support-the-project.md b/docs/docs/overview/support-the-project.md index 7060cef3e1..a439893a7e 100644 --- a/docs/docs/overview/support-the-project.md +++ b/docs/docs/overview/support-the-project.md @@ -16,5 +16,9 @@ Support the project by localizing on [Weblate](https://hosted.weblate.org/projec If you are a programmer or developer, take a look at Immich's [technology stack](/docs/developer/architecture.mdx) and consider fixing bugs or building new features. The team and I are always looking for new contributors. For information about how to contribute as a developer, see the [Developer](/docs/developer/architecture.mdx) section. +## Purchase Immich + +You can also [purchase Immich](https://buy.immich.app), for either one user or your entire server. Building Immich takes a lot of time and effort, and we have full-time engineers working on it to make it as good as we possibly can, so any support is greatly appreciated. Don't worry, all features will be free, forever! Nothing will ever be put behind any paywalls. + [github-issue]: https://github.com/immich-app/immich/issues/new/choose [github-langs]: https://github.com/immich-app/immich/tree/main/mobile/assets/i18n diff --git a/docs/docs/partials/_storage-template.md b/docs/docs/partials/_storage-template.md index b6dcd5ad77..0c668d0a3e 100644 --- a/docs/docs/partials/_storage-template.md +++ b/docs/docs/partials/_storage-template.md @@ -31,5 +31,5 @@ Immich also provides a mechanism to migrate between templates so that if the tem If you want to store assets in album folders, but you also have assets that do not belong to any album, you can use `{{#if album}}`, `{{else}}` and `{{/if}}` to create a conditional statement. For example, the following template will store assets in album folders if they belong to an album, and in a folder named "Other/Month" if they do not belong to an album: ``` -{{y}}/{{#if album}}{{album}}{{else}}Other/{{MM}}{{/if}}/{{filename}} +{{y}}/{{#if album}}{{album}}{{else}}Other{{/if}}/{{MM}}/{{filename}} ``` diff --git a/docs/package-lock.json b/docs/package-lock.json index 01bf257fde..95173daad9 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -16092,9 +16092,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", - "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "version": "3.4.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", + "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 159e0d30e7..cc2128a989 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -15,7 +15,7 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@types/oidc-provider": "^8.5.1", "@types/pg": "^8.11.0", "@types/pngjs": "^6.0.4", @@ -64,7 +64,7 @@ "@types/cli-progress": "^3.11.0", "@types/lodash-es": "^4.17.12", "@types/mock-fs": "^4.13.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "@vitest/coverage-v8": "^2.0.5", @@ -99,7 +99,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "typescript": "^5.3.3" } }, @@ -1187,13 +1187,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.0.tgz", - "integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==", + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.1.tgz", + "integrity": "sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.48.0" + "playwright": "1.48.1" }, "bin": { "playwright": "cli.js" @@ -1614,9 +1614,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.16.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", - "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "version": "20.16.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.13.tgz", + "integrity": "sha512-GjQ7im10B0labo8ZGXDGROUl9k0BNyDgzfGpb4g/cl+4yYDWVKcozANF4FGr4/p0O/rAkQClM6Wiwkije++1Tg==", "dev": true, "license": "MIT", "dependencies": { @@ -1778,17 +1778,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", + "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/type-utils": "8.10.0", + "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1812,16 +1812,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", + "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4" }, "engines": { @@ -1841,14 +1841,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", + "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1859,14 +1859,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", + "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/utils": "8.10.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1884,9 +1884,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", + "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", "dev": true, "license": "MIT", "engines": { @@ -1898,14 +1898,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", + "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1953,16 +1953,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", + "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1976,13 +1976,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", + "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/types": "8.10.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1994,9 +1994,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.2.tgz", - "integrity": "sha512-b7kHrFrs2urS0cOk5N10lttI8UdJ/yP3nB4JYTREvR5o18cR99yPpK4gK8oQgI42BVv0ILWYUSYB7AXkAUDc0g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.3.tgz", + "integrity": "sha512-2OJ3c7UPoFSmBZwqD2VEkUw6A/tzPF0LmW0ZZhhB8PFxuc+9IBG/FaSM+RLEenc7ljzFvGN+G0nGQoZnh7sy2A==", "dev": true, "license": "MIT", "dependencies": { @@ -2017,8 +2017,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.2", - "vitest": "2.1.2" + "@vitest/browser": "2.1.3", + "vitest": "2.1.3" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -2027,14 +2027,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.2.tgz", - "integrity": "sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz", + "integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -2043,13 +2043,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.2.tgz", - "integrity": "sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz", + "integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "^2.1.0-beta.1", + "@vitest/spy": "2.1.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.11" }, @@ -2057,7 +2057,7 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/spy": "2.1.2", + "@vitest/spy": "2.1.3", "msw": "^2.3.5", "vite": "^5.0.0" }, @@ -2071,9 +2071,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.2.tgz", - "integrity": "sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", + "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2084,13 +2084,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.2.tgz", - "integrity": "sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz", + "integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.2", + "@vitest/utils": "2.1.3", "pathe": "^1.1.2" }, "funding": { @@ -2098,13 +2098,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.2.tgz", - "integrity": "sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz", + "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "magic-string": "^0.30.11", "pathe": "^1.1.2" }, @@ -2113,9 +2113,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.2.tgz", - "integrity": "sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz", + "integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2126,13 +2126,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.2.tgz", - "integrity": "sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz", + "integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -4172,9 +4172,9 @@ } }, "node_modules/jose": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.3.tgz", - "integrity": "sha512-egLIoYSpcd+QUF+UHgobt5YzI2Pkw/H39ou9suW687MY6PmCwPmkNV/4TNjn1p2tX5xO3j0d0sq5hiYE24bSlg==", + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.4.tgz", + "integrity": "sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==", "dev": true, "license": "MIT", "funding": { @@ -5162,13 +5162,13 @@ } }, "node_modules/playwright": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.0.tgz", - "integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==", + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.1.tgz", + "integrity": "sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0" + "playwright-core": "1.48.1" }, "bin": { "playwright": "cli.js" @@ -5181,9 +5181,9 @@ } }, "node_modules/playwright-core": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.0.tgz", - "integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==", + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.1.tgz", + "integrity": "sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6407,9 +6407,9 @@ } }, "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz", + "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==", "dev": true, "license": "MIT", "dependencies": { @@ -6467,9 +6467,9 @@ } }, "node_modules/vite-node": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.2.tgz", - "integrity": "sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz", + "integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==", "dev": true, "license": "MIT", "dependencies": { @@ -6504,19 +6504,19 @@ } }, "node_modules/vitest": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.2.tgz", - "integrity": "sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz", + "integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.2", - "@vitest/mocker": "2.1.2", - "@vitest/pretty-format": "^2.1.2", - "@vitest/runner": "2.1.2", - "@vitest/snapshot": "2.1.2", - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/expect": "2.1.3", + "@vitest/mocker": "2.1.3", + "@vitest/pretty-format": "^2.1.3", + "@vitest/runner": "2.1.3", + "@vitest/snapshot": "2.1.3", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "debug": "^4.3.6", "magic-string": "^0.30.11", @@ -6527,7 +6527,7 @@ "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.2", + "vite-node": "2.1.3", "why-is-node-running": "^2.3.0" }, "bin": { @@ -6542,8 +6542,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.2", - "@vitest/ui": "2.1.2", + "@vitest/browser": "2.1.3", + "@vitest/ui": "2.1.3", "happy-dom": "*", "jsdom": "*" }, diff --git a/e2e/package.json b/e2e/package.json index 7a001bfe85..d67aa49f5d 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -25,7 +25,7 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@types/oidc-provider": "^8.5.1", "@types/pg": "^8.11.0", "@types/pngjs": "^6.0.4", diff --git a/e2e/src/api/specs/library.e2e-spec.ts b/e2e/src/api/specs/library.e2e-spec.ts index fe0b4f2bd4..bf0bd4a9c6 100644 --- a/e2e/src/api/specs/library.e2e-spec.ts +++ b/e2e/src/api/specs/library.e2e-spec.ts @@ -633,6 +633,29 @@ describe('/libraries', () => { }); }); + it("should fail if path isn't absolute", async () => { + const pathToTest = `relative/path`; + + const cwd = process.cwd(); + // Create directory in cwd + utils.createDirectory(`${cwd}/${pathToTest}`); + + const response = await utils.validateLibrary(admin.accessToken, library.id, { + importPaths: [pathToTest], + }); + + utils.removeDirectory(`${cwd}/${pathToTest}`); + + expect(response.importPaths?.length).toEqual(1); + const pathResponse = response?.importPaths?.at(0); + + expect(pathResponse).toEqual({ + importPath: pathToTest, + isValid: false, + message: expect.stringMatching('Import path must be absolute, try /usr/src/app/relative/path'), + }); + }); + it('should fail if path is a file', async () => { const pathToTest = `${testAssetDirInternal}/albums/nature/el_torcal_rocks.jpg`; diff --git a/web/src/lib/i18n/af.json b/i18n/af.json similarity index 100% rename from web/src/lib/i18n/af.json rename to i18n/af.json diff --git a/web/src/lib/i18n/ar.json b/i18n/ar.json similarity index 99% rename from web/src/lib/i18n/ar.json rename to i18n/ar.json index 74b4cb4b85..7529ef82ca 100644 --- a/web/src/lib/i18n/ar.json +++ b/i18n/ar.json @@ -51,8 +51,8 @@ "external_library_created_at": "مكتبة خارجية (أُنشئت في {date})", "external_library_management": "إدارة المكتبة الخارجية", "face_detection": "إ‏كتشاف الوجوه", - "face_detection_description": "اكتشف الوجوه في المحتويات باستخدام التعلم الآلي. بالنسبة للفيديوهات، سيتم فقط استخدام الصورة المصغرة. خيار \"الكل\" يعيد معالجة كل المحتويات. خيار \"مفقود\" يضع في قائمة الإنتظار المحتويات التي لم تعالج بعد. سيتم وضع الوجوه المكتشفة في قائمة إنتظار التعرف على الوجه بعد اكتمال اكتشاف الوجه، مما يجمعها بأشخاص موجودين أو جدد.", - "facial_recognition_job_description": "تجميع الوجوه المكتشفة كأشخاص. يتم تنفيذ هذه الخطوة بعد اكتمال اكتشاف الوجه. خيار \"الكل\" يعيد تجميع جميع الوجوه. خيار \"المفقود\" يضع في قائمة الانتظار الوجوه التي لم يتم تعيين شخص لها.", + "face_detection_description": "اكتشف الوجوه في الأصول باستخدام التعلم الآلي. بالنسبة لمقاطع الفيديو، يتم اعتبار الصورة المصغرة فقط. \"تحديث\" (إعادة) معالجة جميع الأصول. \"إعادة تعيين\" تمسح أيضًا جميع بيانات الوجوه الحالية. \"مفقود\" يضع الأصول التي لم تتم معالجتها بعد في قائمة الانتظار. سيتم وضع الوجوه المكتشفة في قائمة الانتظار للتعرف على الوجه بعد اكتمال اكتشاف الوجه، وتجميعها في أشخاص موجودين أو جدد.", + "facial_recognition_job_description": "تجميع الوجوه المكتشفة كأشخاص. يتم تنفيذ هذه الخطوة بعد اكتمال اكتشاف الوجه. خيار \"إعادة التعيين\" يعيد تجميع جميع الوجوه. خيار \"المفقود\" يضع في قائمة الانتظار الوجوه التي لم يتم تعيين شخص لها.", "failed_job_command": "فشل الأمر {command} للمهمة: {job}", "force_delete_user_warning": "تحذير: سيؤدي ذلك إلى إزالة المستخدم وجميع محتوياته على الفور. لا يمكن التراجع عن هذا الإجراء ولا يمكن استرداد الملفات.", "forcing_refresh_library_files": "إجبار التحديث لجميع ملفات المكتبة", @@ -1090,11 +1090,13 @@ "recent_searches": "عمليات البحث الأخيرة", "refresh": "تحديث", "refresh_encoded_videos": "تحديث مقاطع الفيديو المشفرة", + "refresh_faces": "تحديث الوجوه", "refresh_metadata": "تحديث البيانات الوصفية", "refresh_thumbnails": "تحديث الصور المصغرة", "refreshed": "تم التحديث", "refreshes_every_file": "إعادة قراءة كافة الملفات الموجودة والجديدة", "refreshing_encoded_video": "جارٍ تحديث الفيديو المرمز", + "refreshing_faces": "جاري تحديث الوجوه", "refreshing_metadata": "جارٍ تحديث البيانات الوصفية", "regenerating_thumbnails": "جارٍ تجديد الصور المصغرة", "remove": "إزالة", @@ -1363,6 +1365,8 @@ "version": "الإصدار", "version_announcement_closing": "صديقك، أليكس", "version_announcement_message": "مرحباً يا صديقي، هنالك نسخة جديدة من التطبيق. خذ وقتك لزيارة ملاحظات الإصدار والتأكد من أن ملف docker-compose.yml وإعداد .env مُحدّثين لتجنب أي إعدادات خاطئة، خاصةً إذا كنت تستخدم WatchTower أو أي آلية تقوم بتحديث التطبيق تلقائياً.", + "version_history": "تاريخ الإصدار", + "version_history_item": "تم تثبيت {version} في {date}", "video": "فيديو", "video_hover_setting": "تشغيل الصورة المصغرة للفيديو عند التمرير", "video_hover_setting_description": "تشغيل الصورة المصغرة للفيديو عند تحريك الماوس فوق العنصر. حتى عند التعطيل، يمكن بدء التشغيل عن طريق التمرير فوق رمز التشغيل.", diff --git a/web/src/lib/i18n/az.json b/i18n/az.json similarity index 100% rename from web/src/lib/i18n/az.json rename to i18n/az.json diff --git a/web/src/lib/i18n/be.json b/i18n/be.json similarity index 100% rename from web/src/lib/i18n/be.json rename to i18n/be.json diff --git a/web/src/lib/i18n/bg.json b/i18n/bg.json similarity index 100% rename from web/src/lib/i18n/bg.json rename to i18n/bg.json diff --git a/web/src/lib/i18n/bi.json b/i18n/bi.json similarity index 100% rename from web/src/lib/i18n/bi.json rename to i18n/bi.json diff --git a/web/src/lib/i18n/ca.json b/i18n/ca.json similarity index 99% rename from web/src/lib/i18n/ca.json rename to i18n/ca.json index 18896ad6fd..94b04f2c3c 100644 --- a/web/src/lib/i18n/ca.json +++ b/i18n/ca.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Acceleració de maquinari", "transcoding_hardware_acceleration_description": "Experimental. Molt més ràpid, però tindrà una qualitat més baixa amb la mateixa taxa de bits", "transcoding_hardware_decoding": "Descodificació de maquinari", - "transcoding_hardware_decoding_setting_description": "S'aplica només a NVENC, QSV i RKMPP. Permet l'acceleració d'extrem a extrem en lloc d'accelerar només la codificació. És possible que no funcioni en tots els vídeos.", + "transcoding_hardware_decoding_setting_description": "Habilita l'acceleració d'extrem a extrem en lloc d'accelerar només la codificació. És possible que no funcioni en tots els vídeos.", "transcoding_hevc_codec": "Còdec HEVC", "transcoding_max_b_frames": "Nombre màxim de B-frames", "transcoding_max_b_frames_description": "Els valors més alts milloren l'eficiència de la compressió, però alenteixen la codificació. És possible que no sigui compatible amb l'acceleració de maquinari en dispositius antics. 0 desactiva els B-frames, mentre que -1 estableix aquest valor automàticament.", @@ -872,6 +872,7 @@ "look": "Aspecte", "loop_videos": "Vídeos en bucle", "loop_videos_description": "Habilita la reproducció en bucle del vídeo en els detalls.", + "main_branch_warning": "Esteu usant una versió de desenvolupaent. Recomanem fer servir una versió publicada!", "make": "Fabricant", "manage_shared_links": "Spravovat sdílené odkazy", "manage_sharing_with_partners": "Gestiona la compartició amb els companys", diff --git a/web/src/lib/i18n/cs.json b/i18n/cs.json similarity index 99% rename from web/src/lib/i18n/cs.json rename to i18n/cs.json index 5d2ab17afe..12ba83b8e7 100644 --- a/web/src/lib/i18n/cs.json +++ b/i18n/cs.json @@ -110,7 +110,7 @@ "machine_learning_clip_model_description": "Název CLIP modelu je uvedený zde. Pamatujte, že při změně modelu je nutné znovu spustit úlohu 'Chytré vyhledávání' pro všechny obrázky.", "machine_learning_duplicate_detection": "Kontrola duplicit", "machine_learning_duplicate_detection_enabled": "Povolit kontrolu duplicit", - "machine_learning_duplicate_detection_enabled_description": "Pokud je tato funkce vypnuta, budou identické položky stále duplikovány.", + "machine_learning_duplicate_detection_enabled_description": "Pokud je tato funkce vypnuta, budou identické položky stále deduplikovány.", "machine_learning_duplicate_detection_setting_description": "Použít CLIP embeddings k nalezení pravděpodobných duplicit", "machine_learning_enabled": "Povolit strojové učení", "machine_learning_enabled_description": "Pokud je vypnuto, budou všechny funkce strojového učení vypnuty bez ohledu na níže uvedená nastavení.", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Hardwarová akcelerace", "transcoding_hardware_acceleration_description": "Experimentální; mnohem rychlejší, ale při stejném datovém toku bude mít nižší kvalitu", "transcoding_hardware_decoding": "Hardwarové dekódování", - "transcoding_hardware_decoding_setting_description": "Platí pouze pro NVENC, QSV a RKMPP. Povoluje kompletní akceleraci namísto akcelerace pouze kódování. Nemusí fungovat u všech videí.", + "transcoding_hardware_decoding_setting_description": "Povoluje kompletní akceleraci namísto akcelerace pouze kódování. Nemusí fungovat u všech videí.", "transcoding_hevc_codec": "Kodek HEVC", "transcoding_max_b_frames": "Maximální počet B-snímků", "transcoding_max_b_frames_description": "Vyšší hodnoty zvyšují účinnost komprese, ale zpomalují kódování. Nemusí být kompatibilní s hardwarovou akcelerací na starších zařízeních. Hodnota 0 zakáže B-snímky, zatímco -1 tuto hodnotu nastaví automaticky.", @@ -382,7 +382,7 @@ "all_videos": "Všechna videa", "allow_dark_mode": "Povolit tmavý režim", "allow_edits": "Povolit úpravy", - "allow_public_user_to_download": "Povolit veřejnosti stahování", + "allow_public_user_to_download": "Povolit veřejnosti stahovat", "allow_public_user_to_upload": "Povolit veřejnosti nahrávat", "anti_clockwise": "Proti směru hodinových ručiček", "api_key": "API klíč", @@ -888,6 +888,7 @@ "look": "Zobrazení", "loop_videos": "Videa ve smyčce", "loop_videos_description": "Povolit automatickou smyčku videa v prohlížeči.", + "main_branch_warning": "Používáte vývojovou verzi; důrazně doporučujeme používat verzi z vydání!", "make": "Výrobce", "manage_shared_links": "Spravovat sdílené odkazy", "manage_sharing_with_partners": "Správa sdílení s partnery", diff --git a/web/src/lib/i18n/cv.json b/i18n/cv.json similarity index 77% rename from web/src/lib/i18n/cv.json rename to i18n/cv.json index 33fb160004..8f0581053e 100644 --- a/web/src/lib/i18n/cv.json +++ b/i18n/cv.json @@ -33,12 +33,16 @@ "check_all": "Пурне те тӗрӗслӗр", "cleared_jobs": "Ӗҫсене тасатнӑ:{job}", "confirm_email_below": "Ҫирӗплетес тесен, аяларах «{email}» кӗртӗр", + "confirm_reprocess_all_faces": "Пӗтӗм сӑнӗсене тепӗр хут палӑртас килет тесе шанатӑр-и? Ҫавӑн пекех ятсене пур ҫынран та хуратӗҫ.", "create_job": "Ӗҫе ту", "disable_login": "Кӗме чарӑр", "duplicate_detection_job_description": "Пӗр пек ӳкерчӗксене тупма машинӑллӑ вӗренӗве ӗҫлеттерӗр. Ӑслӑ шыравпа усӑ кураҫҫӗ", "face_detection": "Пит-куҫа тупасси", "force_delete_user_warning": "ПУЛТАРУЛӐХ: Ку усӑ куракана тата мӗнпур ресурса ҫийӗнчех кӑларса пӑрахасси патне илсе ҫитерӗ. Кӑна пӑрахӑҫлама май ҫук, файлсене те юсаса пӗтереймеҫҫӗ.", "image_format": "Тулашлăх", + "image_preview_description": "Вӑтам пысӑкӑш ӳкерчӗк, уйрӑм метаданнӑйсем, пӗр объекта пӑхнӑ чухне тата машинӑллӑ вӗренӳре усӑ кураҫҫӗ", + "image_preview_quality_description": "1-100 таран малтанхи пахалӑх. Ҫӳллӗреххи лайӑхрах, анчах та пысӑкрах файлсем туса кӑларать тата приложенисен хуравлӑхне чакарма пултарать. Пӗчӗк хак лартни машинӑллӑ вӗренӳ пахалӑхне витӗм кӳме пултарать.", + "image_preview_title": "Малтанлӑха пӑхмалли ӗнерлевсем", "image_quality": "Пахалӑх", "image_resolution": "Виҫе" } diff --git a/web/src/lib/i18n/da.json b/i18n/da.json similarity index 100% rename from web/src/lib/i18n/da.json rename to i18n/da.json diff --git a/web/src/lib/i18n/de.json b/i18n/de.json similarity index 91% rename from web/src/lib/i18n/de.json rename to i18n/de.json index fa61e34704..f92da539a6 100644 --- a/web/src/lib/i18n/de.json +++ b/i18n/de.json @@ -28,7 +28,7 @@ "added_to_favorites_count": "{count, number} zu Favoriten hinzugefügt", "admin": { "add_exclusion_pattern_description": "Ausschlussmuster hinzufügen. Platzhalter, wie *, **, und ? werden unterstützt. Um alle Dateien in einem Verzeichnis namens „Raw\" zu ignorieren, „**/Raw/**“ verwenden. Um alle Dateien zu ignorieren, die auf „.tif“ enden, „**/*.tif“ verwenden. Um einen absoluten Pfad zu ignorieren, „/pfad/zum/ignorieren/**“ verwenden.", - "asset_offline_description": "Diese Datei einer externen Bibliotheks befindet sich nicht mehr auf der Festplatte und wurde in den Papierkorb verschoben. Wenn die Datei innerhalb der Bibliothek verschoben wurde, überprüfe deine Zeitleiste auf die neue entsprechende Datei. Um diese Datei wiederherzustellen, stelle bitte sicher, dass Immich auf den unten stehenden Dateipfad zugreifen und die Bibliothek scannen kann.", + "asset_offline_description": "Diese Datei einer externen Bibliothek befindet sich nicht mehr auf der Festplatte und wurde in den Papierkorb verschoben. Falls die Datei innerhalb der Bibliothek verschoben wurde, überprüfe deine Zeitleiste auf die neue entsprechende Datei. Um diese Datei wiederherzustellen, stelle bitte sicher, dass Immich auf den unten stehenden Dateipfad zugreifen kann und scanne die Bibliothek.", "authentication_settings": "Authentifizierungseinstellungen", "authentication_settings_description": "Passwort-, OAuth- und sonstigen Authentifizierungseinstellungen verwalten", "authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.", @@ -38,21 +38,21 @@ "cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}", "config_set_by_file": "Ist derzeit in einer Konfigurationsdatei festgelegt", "confirm_delete_library": "Bist du sicher, dass du die Bibliothek {library} löschen willst?", - "confirm_delete_library_assets": "Bist du sicher, dass du diese Bibliothek löschen willst? Dies löscht alle {count, plural, one {# enthaltenes Objekt} other {alle # enthaltenen Objekte}} aus Immich und kann nicht rückgängig gemacht werden. Die Dateien bleiben auf der Festplatte erhalten.", - "confirm_email_below": "Bestätige, indem du \"{email}\" unten eingibst", + "confirm_delete_library_assets": "Bist du sicher, dass du diese Bibliothek löschen willst? Dies löscht {count, plural, one {# enthaltenes Objekt} other {alle # enthaltenen Objekte}} aus Immich und kann nicht rückgängig gemacht werden. Die Dateien bleiben auf der Festplatte erhalten.", + "confirm_email_below": "Bestätige, indem du unten \"{email}\" eingibst", "confirm_reprocess_all_faces": "Bist du sicher, dass du alle Gesichter erneut verarbeiten möchtest? Dies löscht auch alle bereits benannten Personen.", "confirm_user_password_reset": "Bist du sicher, dass du das Passwort für {user} zurücksetzen möchtest?", "create_job": "Aufgabe erstellen", "crontab_guru": "Crontab Guru", "disable_login": "Login deaktvieren", "disabled": "Deaktiviert", - "duplicate_detection_job_description": "Diese Aufgabe führt das maschinelle Lernen für jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der Smart Search Technologie", - "exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn Sie Ordner haben, die Dateien enthalten, die Sie nicht importieren möchten, wie z. B. RAW-Dateien.", + "duplicate_detection_job_description": "Diese Aufgabe führt das maschinelle Lernen für jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche", + "exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren möchtest, wie z. B. RAW-Dateien.", "external_library_created_at": "Externe Bibliothek (erstellt am {date})", - "external_library_management": "Externe Bibliotheksverwaltung", + "external_library_management": "Verwaltung externer Bibliotheken", "face_detection": "Gesichtserkennung", "face_detection_description": "Diese Aufgabe erkennt Gesichter in Dateien mittels maschinellen Lernens. Bei Videos wird nur die Miniaturansicht verwendet. „Aktualisieren“ verarbeitet alle Dateien neu. „Zurücksetzen“ setzt zusätzlich alle Gesichter zurück. „Fehlende“ stellt nur nicht verarbeitete Dateien in die Warteschlange. Erkannte Gesichter werden zur Gruppierung in bestehende oder neue Personen in die Warteschlange gestellt.", - "facial_recognition_job_description": "Diese Aufgabe gruppiert erkannte Gesichter zu Personen nach der Gesichtserkennung. „Zurücksetzen“ clustert alle Gesichter neu, während „Fehlende“ Gesichter ohne Zuordnung in die Warteschlange stellt.", + "facial_recognition_job_description": "Diese Aufgabe gruppiert im Anschluss an die Gesichtserkennung die erkannten Gesichter zu Personen. „Zurücksetzen“ gruppiert alle Gesichter neu, während „Fehlende“ Gesichter ohne Zuordnung in die Warteschlange stellt.", "failed_job_command": "Befehl {command} ist für Aufgabe {job} fehlgeschlagen", "force_delete_user_warning": "WARNUNG: Diese Aktion löscht sofort den Benutzer und all seine Dateien. Dies kann nicht rückgängig gemacht werden und die Dateien können nicht wiederhergestellt werden.", "forcing_refresh_library_files": "Erneutes Laden aller Bibliotheksdateien erzwingen", @@ -80,21 +80,21 @@ "image_thumbnail_resolution": "Miniaturansichts-Auflösung", "image_thumbnail_resolution_description": "Dies wird bei der Anzeige von Bildergruppen („Zeitleiste“, „Albumansicht“ usw.) verwendet. Höhere Auflösungen können mehr Details beibehalten, benötigen aber mehr Zeit für die Kodierung, haben größere Dateigrößen und können die Reaktionsfähigkeit der App beeinträchtigen.", "image_thumbnail_title": "Miniaturansicht-Einstellungen", - "job_concurrency": "{job} - (Anzahl gleichzeitiger Prozesse)", - "job_created": "Job erstellt", - "job_not_concurrency_safe": "Dieser Job ist nicht parallelisierungssicher.", - "job_settings": "Job-Einstellungen", - "job_settings_description": "Gleichzeitige Job-Prozessen verwalten", - "job_status": "Job-Status", + "job_concurrency": "{job} (Anzahl gleichzeitiger Prozesse)", + "job_created": "Aufgabe erstellt", + "job_not_concurrency_safe": "Diese Aufgabe ist nicht parallelisierungssicher.", + "job_settings": "Aufgaben-Einstellungen", + "job_settings_description": "Gleichzeitige Aufgaben-Prozesse verwalten", + "job_status": "Aufgaben-Status", "jobs_delayed": "{jobCount, plural, other {# verzögert}}", "jobs_failed": "{jobCount, plural, other {# fehlgeschlagen}}", "library_created": "Bibliothek erstellt: {library}", "library_cron_expression": "Cron-Ausdruck", - "library_cron_expression_description": "Legen Sie das Überprüfungsintervall mit Hilfe des cron-Formats fest. Für weitere Informationen siehe z.B. Crontab Guru", + "library_cron_expression_description": "Lege das Überprüfungsintervall mit Hilfe des cron-Formats fest. Für weitere Informationen siehe z.B. Crontab Guru", "library_cron_expression_presets": "Cron-Expression Voreinstellungen", "library_deleted": "Bibliothek gelöscht", "library_import_path_description": "Gib einen Ordner für den Import an. Dieser Ordner, einschließlich der Unterordner, wird nach Bildern und Videos durchsucht.", - "library_scanning": "Periodisches scannen", + "library_scanning": "Periodisches Scannen", "library_scanning_description": "Regelmäßiges Durchsuchen der Bibliothek einstellen", "library_scanning_enable_description": "Regelmäßiges Scannen der Bibliothek aktivieren", "library_settings": "Externe Bibliothek", @@ -104,12 +104,12 @@ "library_watching_settings": "Bibliotheksüberwachung (EXPERIMENTELL)", "library_watching_settings_description": "Automatisch auf geänderte Dateien prüfen", "logging_enable_description": "Aktiviere Logging", - "logging_level_description": "Wenn aktiviert, welches Log Level genutzt wird.", + "logging_level_description": "Wenn aktiviert, welches Log-Level genutzt wird.", "logging_settings": "Protokollierung", "machine_learning_clip_model": "CLIP-Modell", - "machine_learning_clip_model_description": "Der Name eines CLIP-Modells, welches \"hier\" aufgeführt ist. Beachte, dass du den Job \"Intelligente Suche\" für alle Bilder erneut ausführen musst, wenn du das Modell wechselst.", - "machine_learning_duplicate_detection": "Duplikats-Erkennung", - "machine_learning_duplicate_detection_enabled": "Duplikat-Erkennung aktivieren", + "machine_learning_clip_model_description": "Der Name eines CLIP-Modells, welches hier aufgeführt ist. Beachte, dass du die Aufgabe \"Intelligente Suche\" für alle Bilder erneut ausführen musst, wenn du das Modell wechselst.", + "machine_learning_duplicate_detection": "Duplikaterkennung", + "machine_learning_duplicate_detection_enabled": "Duplikaterkennung aktivieren", "machine_learning_duplicate_detection_enabled_description": "Falls diese Option deaktiviert ist, werden exakt identische Dateien dennoch de-dupliziert.", "machine_learning_duplicate_detection_setting_description": "Verwendung von CLIP-Embeddings zum Erkennen möglicher Duplikate", "machine_learning_enabled": "Maschinelles Lernen aktivieren", @@ -117,21 +117,21 @@ "machine_learning_facial_recognition": "Gesichtsidentifikation", "machine_learning_facial_recognition_description": "Erkenne, identifiziere und gruppiere Gesichter in Bildern", "machine_learning_facial_recognition_model": "Gesichtserkennungs-Modell", - "machine_learning_facial_recognition_model_description": "Die Modelle sind in absteigender Reihenfolge ihrer Größe aufgeführt. Größere Modelle sind langsamer und verbrauchen mehr Speicher, liefern aber bessere Ergebnisse. Bitte beachte dabei, dass du den Gesichtserkennungsjob für alle Bilder neu starten musst, wenn du ein Modell änderst.", + "machine_learning_facial_recognition_model_description": "Die Modelle sind in absteigender Reihenfolge ihrer Größe aufgeführt. Größere Modelle sind langsamer und verbrauchen mehr Speicher, liefern aber bessere Ergebnisse. Bitte beachte dabei, dass du die Gesichtserkennungsaufgabe für alle Bilder neu starten musst, wenn du ein Modell änderst.", "machine_learning_facial_recognition_setting": "Gesichtserkennung aktivieren", "machine_learning_facial_recognition_setting_description": "Wenn diese Option deaktiviert ist, werden die Bilder nicht für die Gesichtserkennung kodiert und der Abschnitt „Personen“ auf der Seite „Erkunden“ wird nicht dargestellt.", "machine_learning_max_detection_distance": "Maximaler Erkennungsabstand", - "machine_learning_max_detection_distance_description": "Maximaler Unterschied zwischen zwei Bildern, um sie als Duplikate zu betrachten, im Bereich von 0,001-0,1. Bei höheren Werten werden mehr Duplikate erkannt, aber es kann zu falsch positiven Ergebnissen kommen.", + "machine_learning_max_detection_distance_description": "Maximaler Unterschied zwischen zwei Bildern, um sie als Duplikate zu betrachten, im Bereich von 0,001-0,1. Bei höheren Werten werden mehr Duplikate erkannt, aber es kann zu falsch-positiven Ergebnissen kommen.", "machine_learning_max_recognition_distance": "Maximaler Erkennungsabstand", "machine_learning_max_recognition_distance_description": "Maximaler Abstand zwischen zwei Gesichtern, die als dieselbe Person angesehen werden, von 0-2. Ein niedrigerer Wert kann verhindern, dass zwei Personen als dieselbe Person eingestuft werden, während ein höherer Wert verhindern kann, dass ein und dieselbe Person als zwei verschiedene Personen eingestuft wird. Bitte beachte dabei, dass es einfacher ist, zwei Personen zu verschmelzen, als eine Person in zwei zu teilen, also wähle nach Möglichkeit einen niedrigeren Schwellenwert.", "machine_learning_min_detection_score": "Minimale Erkennungsrate", "machine_learning_min_detection_score_description": "Minimale Konfidenzrate für die Erkennung eines Gesichts von 0-1. Bei niedrigeren Werten werden mehr Gesichter erkannt, aber es kann zu falsch-positiven Ergebnissen kommen.", "machine_learning_min_recognized_faces": "Mindestens erkannte Gesichter", - "machine_learning_min_recognized_faces_description": "Die Mindestanzahl von erkannten Gesichtern, damit eine Person erstellt werden kann. Eine Erhöhung dieses Wertes macht die Gesichtserkennung präziser, erhöht aber die Wahrscheinlichkeit, dass ein Gesicht nicht zu einer Person zugeordnet werden kann.", + "machine_learning_min_recognized_faces_description": "Die Mindestanzahl von erkannten Gesichtern, damit eine Person erstellt werden kann. Eine Erhöhung dieses Wertes macht die Gesichtserkennung präziser, erhöht aber die Wahrscheinlichkeit, dass ein Gesicht nicht zu einer Person zugeordnet wird.", "machine_learning_settings": "Einstellungen für maschinelles Lernen", - "machine_learning_settings_description": "Funktionen und Einstellungen für das maschinelle Lernen verwalten", + "machine_learning_settings_description": "Funktionen und Einstellungen des maschinellen Lernens verwalten", "machine_learning_smart_search": "Intelligente Suche", - "machine_learning_smart_search_description": "Semantische Bildsuche mit CLIP-Einbettungen", + "machine_learning_smart_search_description": "Semantische Bildsuche mittels CLIP-Einbettungen", "machine_learning_smart_search_enabled": "Intelligente Suche aktivieren", "machine_learning_smart_search_enabled_description": "Ist diese Option deaktiviert, werden die Bilder nicht für die intelligente Suche verwendet.", "machine_learning_url_description": "Server-URL für maschinelles Lernen", @@ -139,31 +139,31 @@ "manage_log_settings": "Log-Einstellungen verwalten", "map_dark_style": "Dunkler Stil", "map_enable_description": "Kartenfunktionen aktivieren", - "map_gps_settings": "Karten & GPS Einstellungen", - "map_gps_settings_description": "Karten & GPS Einstellungen verwalten", + "map_gps_settings": "Karten- & GPS-Einstellungen", + "map_gps_settings_description": "Karten- & GPS-Einstellungen verwalten", "map_implications": "Die Kartenfunktion verwendet einen externen Tile-Service (tiles.immich.cloud)", "map_light_style": "Heller Stil", - "map_manage_reverse_geocoding_settings": "Einstellungen für die Umgekehrte Geokodierung verwalten", + "map_manage_reverse_geocoding_settings": "Einstellungen für die umgekehrte Geokodierung verwalten", "map_reverse_geocoding": "Umgekehrte Geokodierung", "map_reverse_geocoding_enable_description": "Umgekehrte Geokodierung aktivieren", - "map_reverse_geocoding_settings": "Einstellungen für Umgekehrte Geokodierung", - "map_settings": "Karten", - "map_settings_description": "Karten- und GPS Einstellungen verwalten", + "map_reverse_geocoding_settings": "Einstellungen für umgekehrte Geokodierung", + "map_settings": "Karte", + "map_settings_description": "Karten- und GPS-Einstellungen verwalten", "map_style_description": "URL zu einem style.json Karten-Theme", "metadata_extraction_job": "Metadaten extrahieren", "metadata_extraction_job_description": "Extrahieren von Metadaten, wie zum Beispiel GPS, Gesichtern und Auflösung aus jeder Datei", "metadata_faces_import_setting": "Import von Gesichtern aktivieren", - "metadata_faces_import_setting_description": "Gesichter aus EXIF Daten des Bildes und Sidecar Dateien importieren", - "metadata_settings": "Metadaten Einstellungen", - "metadata_settings_description": "Metadaten Einstellungen verwalten", + "metadata_faces_import_setting_description": "Gesichter aus EXIF-Daten des Bildes und Sidecar-Dateien importieren", + "metadata_settings": "Metadaten-Einstellungen", + "metadata_settings_description": "Metadaten-Einstellungen verwalten", "migration_job": "Migration", "migration_job_description": "Diese Aufgabe migriert Miniaturansichten für Dateien und Gesichter in die neueste Ordnerstruktur", "no_paths_added": "Keine Pfade hinzugefügt", - "no_pattern_added": "Kein Pattern hinzugefügt", - "note_apply_storage_label_previous_assets": "Hinweis: Um das Storage Label auf die vorher hochgeladenen Dateien anzuwenden, starte den", + "no_pattern_added": "Kein Ausschlussmuster hinzugefügt", + "note_apply_storage_label_previous_assets": "Hinweis: Um den Speicherpfad auf die vorher hochgeladenen Dateien anzuwenden, starte den", "note_cannot_be_changed_later": "HINWEIS: Dies kann später nicht mehr geändert werden!", "note_unlimited_quota": "Hinweis: 0 eingeben für unlimitiertes Kontingent", - "notification_email_from_address": "Von", + "notification_email_from_address": "Absenderadresse", "notification_email_from_address_description": "E-Mail-Adresse des Senders, zum Beispiel: \"Immich Photo Server \"", "notification_email_host_description": "Host des E-Mail-Servers (z.B. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignoriere Zertifikats-Fehler", @@ -178,13 +178,13 @@ "notification_email_username_description": "Benutzername, der bei der Anmeldung am E-Mail-Server verwendet wird", "notification_enable_email_notifications": "E-Mail-Benachrichtigungen aktivieren", "notification_settings": "Benachrichtigungseinstellungen", - "notification_settings_description": "Eenachrichtigungseinstellungen (inkl. E-Mail) verwalten", + "notification_settings_description": "Benachrichtigungseinstellungen (inkl. E-Mail) verwalten", "oauth_auto_launch": "Auto-Start", "oauth_auto_launch_description": "Automatischer Start des OAuth-Anmeldevorgangs beim Aufrufen der Anmeldeseite", "oauth_auto_register": "Automatische Registrierung", "oauth_auto_register_description": "Automatische Registrierung neuer Benutzer nach der OAuth-Anmeldung", - "oauth_button_text": "Button Text", - "oauth_client_id": "Client ID", + "oauth_button_text": "Button-Text", + "oauth_client_id": "Client-ID", "oauth_client_secret": "Client-Geheimnis", "oauth_enable_description": "Anmeldung mit OAuth", "oauth_issuer_url": "Aussteller-URL", @@ -192,7 +192,7 @@ "oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI überschreiben", "oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Provider keine mobile URI wie '{callback}' erlaubt", "oauth_profile_signing_algorithm": "Algorithmus zur Profilsignierung", - "oauth_profile_signing_algorithm_description": "Dieser Algorithmus wird für die für die Signatur des Benutzerprofils verwendet.", + "oauth_profile_signing_algorithm_description": "Dieser Algorithmus wird für die Signatur des Benutzerprofils verwendet.", "oauth_scope": "Umfang", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten", @@ -203,11 +203,11 @@ "oauth_storage_quota_claim": "Speicherkontingentangabe", "oauth_storage_quota_claim_description": "Setzen Sie das Speicherkontingent des Benutzers automatisch auf den angegebenen Wert.", "oauth_storage_quota_default": "Standard-Speicherplatzkontingent (GiB)", - "oauth_storage_quota_default_description": "Kontingent in GiB, welcher verwendet werden kann, wenn kein Anspruch erhoben wurde (Gib 0 für einen unbegrenzten Speicherkontingent ein).", + "oauth_storage_quota_default_description": "Kontingent in GiB, das verwendet werden soll, wenn keines übermittelt wird (gib 0 für ein unbegrenztes Kontingent ein).", "offline_paths": "Offline-Pfade", "offline_paths_description": "Die Ergebnisse könnten durch manuelles Löschen von Dateien, die nicht Teil einer externen Bibliothek sind, verursacht sein.", "password_enable_description": "Login mit E-Mail und Passwort", - "password_settings": "Passwort Login", + "password_settings": "Passwort-Login", "password_settings_description": "Passwort-Anmeldeeinstellungen verwalten", "paths_validated_successfully": "Alle Pfade wurden erfolgreich validiert", "person_cleanup_job": "Personen aufräumen", @@ -225,7 +225,7 @@ "scanning_library": "Bibliothek scannen", "scanning_library_for_changed_files": "Untersuche Bibliothek auf geänderte Dateien", "scanning_library_for_new_files": "Untersuche Bibliothek auf neue Dateien", - "search_jobs": "Jobs suchen...", + "search_jobs": "Aufgaben suchen...", "send_welcome_email": "Begrüssungsmail senden", "server_external_domain_settings": "Externe Domain", "server_external_domain_settings_description": "Domäne für öffentlich freigegebene Links, einschließlich http(s)://", @@ -236,7 +236,7 @@ "sidecar_job": "Filialdatei-Metadaten", "sidecar_job_description": "Durch diese Aufgabe werden Filialdatei-Metadaten im Dateisystem entdeckt oder synchronisiert", "slideshow_duration_description": "Dauer der Anzeige jedes Bildes in Sekunden", - "smart_search_job_description": "Diese Aufgabe wendet das maschinelles Lernen auf Dateien an, um die intelligente Suche zu ermöglichen", + "smart_search_job_description": "Diese Aufgabe wendet das maschinelle Lernen auf Dateien an, um die intelligente Suche zu ermöglichen", "storage_template_date_time_description": "Der Erstellungszeitstempel der Datei wird für die Datums- und Uhrzeitinformation verwendet", "storage_template_date_time_sample": "Beispielzeitpunkt {date}", "storage_template_enable_description": "Speichervorlagen-Engine aktivieren", @@ -245,13 +245,13 @@ "storage_template_migration": "Migration von Speichervorlagen", "storage_template_migration_description": "Diese Aufgabe wendet die aktuelle {template} auf zuvor hochgeladene Dateien an", "storage_template_migration_info": "Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den {job} aus.", - "storage_template_migration_job": "Speichervorlagenmigrations-Job", - "storage_template_more_details": "Weitere Details zu dieser Funktion finden Sie unter Speichervorlage und dessen Implikationen", + "storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe", + "storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter Speichervorlage und dessen Implikationen", "storage_template_onboarding_description": "Wenn aktiviert, sortiert diese Funktion Dateien automatisch basierend auf einer benutzerdefinierten Vorlage. Aufgrund von Stabilitätsproblemen ist die Funktion standardmäßig deaktiviert. Weitere Informationen findest du in der Dokumentation.", - "storage_template_path_length": "Ungefähres Pfad Längen Limit: {length, number}/{limit, number}", + "storage_template_path_length": "Ungefähres Pfadlängen-Limit: {length, number}/{limit, number}", "storage_template_settings": "Speichervorlage", "storage_template_settings_description": "Die Ordnerstruktur und den Dateinamen der hochgeladenen Datei verwalten", - "storage_template_user_label": "{label} is das Speicher-Label des Benutzers", + "storage_template_user_label": "{label} is die Speicherpfadbezeichnung des Benutzers", "system_settings": "Systemeinstellungen", "tag_cleanup_job": "Tags aufräumen", "theme_custom_css_settings": "Benutzerdefiniertes CSS", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Hardware-Beschleunigung", "transcoding_hardware_acceleration_description": "Experimentell; viel schneller, aber bei gleicher Bitrate mit geringerer Qualität", "transcoding_hardware_decoding": "Hardware-Dekodierung", - "transcoding_hardware_decoding_setting_description": "Nur gültig für NVENC, QSV und RKMPP. Ermöglicht eine Ende-zu-Ende-Beschleunigung, anstatt nur die Codierung zu beschleunigen. Dies funktioniert möglicherweise nicht bei allen Videos.", + "transcoding_hardware_decoding_setting_description": "Ermöglicht eine Ende-zu-Ende-Beschleunigung, anstatt nur die Codierung zu beschleunigen. Dies funktioniert möglicherweise nicht bei allen Videos.", "transcoding_hevc_codec": "HEVC-Codec", "transcoding_max_b_frames": "Maximale B-Frames", "transcoding_max_b_frames_description": "Höhere Werte verbessern die Komprimierungseffizienz, verlangsamen aber die Kodierung. Ist möglicherweise nicht mit der Hardware-Beschleunigung älterer Geräte kompatibel. 0 deaktiviert die B-Frames, während -1 diesen Wert automatisch setzt.", @@ -327,7 +327,7 @@ "trash_settings": "Papierkorb-Einstellungen", "trash_settings_description": "Papierkorb-Einstellungen verwalten", "untracked_files": "Unverfolgte Dateien", - "untracked_files_description": "Diese Dateien werden nicht von der Application getrackt. Sie können das Ergebnis fehlgeschlagener Verschiebungen, unterbrochener Uploads oder aufgrund eines Fehlers sein", + "untracked_files_description": "Diese Dateien werden nicht von der Anwendung getrackt. Sie können das Ergebnis fehlgeschlagener Verschiebungen, unterbrochener Uploads oder aufgrund eines Fehlers sein", "user_cleanup_job": "Benutzer aufräumen", "user_delete_delay": "Das Konto und die Dateien von {user} werden in {delay, plural, one {einem Tag} other {# Tagen}} für eine permanente Löschung geplant.", "user_delete_delay_settings": "Verzögerung für das Löschen von Benutzern", @@ -360,12 +360,12 @@ "album_added_notification_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn du zu einem freigegebenen Album hinzugefügt wurdest", "album_cover_updated": "Album-Cover aktualisiert", "album_delete_confirmation": "Bist du sicher, dass du das Album {album} löschen willst?", - "album_delete_confirmation_description": "Wenn dieses Album geteilt wurde, können andere Benutzer nicht mehr darauf zugreifen.", + "album_delete_confirmation_description": "Falls dieses Album geteilt wurde, können andere Benutzer nicht mehr darauf zugreifen.", "album_info_updated": "Album-Infos aktualisiert", "album_leave": "Album verlassen?", "album_leave_confirmation": "Bist du sicher, dass du das Album {album} verlassen willst?", - "album_name": "Album Name", - "album_options": "Album Optionen", + "album_name": "Albumname", + "album_options": "Albumoptionen", "album_remove_user": "Nutzer entfernen?", "album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?", "album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.", @@ -373,7 +373,7 @@ "album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält", "album_user_left": "{album} verlassen", "album_user_removed": "{user} entfernt", - "album_with_link_access": "Lass jeden mit dem Link Fotos und Personen in diesem Album sehen.", + "album_with_link_access": "Lass jeden mit dem Link die Fotos und Personen in diesem Album sehen.", "albums": "Alben", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Alben}}", "all": "Alle", @@ -396,7 +396,7 @@ "archive_size": "Archivgröße", "archive_size_description": "Archivgröße für Downloads konfigurieren (in GiB)", "archived": "Archiviert", - "archived_count": "{count, plural, other {# Archiviert}}", + "archived_count": "{count, plural, other {# archiviert}}", "are_these_the_same_person": "Ist das dieselbe Person?", "are_you_sure_to_do_this": "Bist du sicher, dass du das tun willst?", "asset_added_to_album": "Zum Album hinzugefügt", @@ -414,7 +414,7 @@ "assets": "Dateien", "assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefügt", "assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt", - "assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {{name}} other {neuen Album}} hinzugefügt", + "assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {{name}} other {neuem Album}} hinzugefügt", "assets_count": "{count, plural, one {# Datei} other {# Dateien}}", "assets_moved_to_trash": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben", "assets_moved_to_trash_count": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben", @@ -427,16 +427,16 @@ "authorized_devices": "Verwendete Geräte", "back": "Zurück", "back_close_deselect": "Zurück, Schließen oder Abwählen", - "backward": "Zurück", + "backward": "Rückwärts", "birthdate_saved": "Geburtsdatum erfolgreich gespeichert", "birthdate_set_description": "Das Geburtsdatum wird verwendet, um das Alter dieser Person zum Zeitpunkt eines Fotos zu berechnen.", "blurred_background": "Unscharfer Hintergrund", "bugs_and_feature_requests": "Fehler & Verbesserungsvorschläge", "build": "Build", "build_image": "Build Abbild", - "bulk_delete_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien}} gemeinsam löschen möchtest? Dabei wird die größte Datei jeder Gruppe behalten und alle anderen Duplikate dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden!", + "bulk_delete_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien gemeinsam}} löschen möchtest? Dabei wird die größte Datei jeder Gruppe behalten und alle anderen Duplikate dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden!", "bulk_keep_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien}} behalten möchtest? Dies wird alle Duplikat-Gruppen auflösen ohne etwas zu löschen.", - "bulk_trash_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien}} gemeinsam in den Papierkorb verschieben möchtest? Dies wird die größte Datei jeder Gruppe behalten und alle anderen Duplikate in den Papierkorb verschieben.", + "bulk_trash_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien gemeinsam}} in den Papierkorb verschieben möchtest? Dies wird die größte Datei jeder Gruppe behalten und alle anderen Duplikate in den Papierkorb verschieben.", "buy": "Immich erwerben", "camera": "Kamera", "camera_brand": "Kamera-Marke", @@ -455,7 +455,7 @@ "change_location": "Ort ändern", "change_name": "Name ändern", "change_name_successfully": "Name wurde erfolgreich geändert", - "change_password": "Passwort Ändern", + "change_password": "Passwort ändern", "change_password_description": "Dies ist entweder das erste Mal, dass du dich im System anmeldest, oder es wurde eine Anfrage zur Änderung deines Passworts gestellt. Bitte gib unten dein neues Passwort ein.", "change_your_password": "Ändere dein Passwort", "changed_visibility_successfully": "Die Sichtbarkeit wurde erfolgreich geändert", @@ -471,11 +471,11 @@ "clockwise": "Im Uhrzeigersinn", "close": "Schließen", "collapse": "Zusammenklappen", - "collapse_all": "Alles aufklappen", + "collapse_all": "Alle zusammenklappen", "color": "Farbe", "color_theme": "Farb-Theme", "comment_deleted": "Kommentar gelöscht", - "comment_options": "Kommentar-Optionen", + "comment_options": "Kommentaroptionen", "comments_and_likes": "Kommentare & Likes", "comments_are_disabled": "Kommentare sind deaktiviert", "confirm": "Bestätigen", @@ -486,7 +486,7 @@ "context": "Kontext", "continue": "Fortsetzen", "copied_image_to_clipboard": "Das Bild wurde in die Zwischenablage kopiert.", - "copied_to_clipboard": "In Zwischenablage kopiert!", + "copied_to_clipboard": "In die Zwischenablage kopiert!", "copy_error": "Kopier-Fehler", "copy_file_path": "Dateipfad kopieren", "copy_image": "Bild kopieren", @@ -507,7 +507,7 @@ "create_new_person_hint": "Ausgewählte Dateien einer neuen Person zuweisen", "create_new_user": "Neuen Nutzer erstellen", "create_tag": "Tag erstellen", - "create_tag_description": "Erstelle einen neuen Tag. Für verschachtelte Tags, gib den gesamten Pfad inklusive Slash an.", + "create_tag_description": "Erstelle einen neuen Tag. Für verschachtelte Tags, gib den gesamten Pfad inklusive Schrägstrich an.", "create_user": "Nutzer erstellen", "created": "Erstellt", "current_device": "Aktuelles Gerät", @@ -548,16 +548,16 @@ "display_options": "Anzeigeoptionen", "display_order": "Anzeigereihenfolge", "display_original_photos": "Originale Fotos anzeigen", - "display_original_photos_setting_description": "Bei der Anzeige eines Bildes wird bevorzugt das Originalfoto statt der Miniaturansicht angezeigt, sofern das Original webkompatibel ist. Dies kann zu einer langsameren Ladezeit der Fotos führen.", + "display_original_photos_setting_description": "Bei der Anzeige eines Bildes wird bevorzugt das Originalfoto statt der Miniaturansicht angezeigt, sofern das Original webkompatibel ist. Dies kann zu einer längeren Ladezeit der Fotos führen.", "do_not_show_again": "Diese Nachricht nicht erneut anzeigen", "documentation": "Dokumentation", "done": "Fertig", - "download": "Download", + "download": "Herunterladen", "download_include_embedded_motion_videos": "Eingebettete Videos", "download_include_embedded_motion_videos_description": "Videos, die in Bewegungsfotos eingebettet sind, als separate Datei einfügen", "download_settings": "Download", - "download_settings_description": "Einstellungen für den Dateidownload verwalten", - "downloading": "Downloaden", + "download_settings_description": "Einstellungen für das Herunterladen von Dateien verwalten", + "downloading": "Herunterladen", "downloading_asset_filename": "Datei {filename} wird heruntergeladen", "drop_files_to_upload": "Lade Dateien hoch, indem du sie hierhin ziehst", "duplicates": "Duplikate", @@ -589,7 +589,7 @@ "edit_user": "Nutzer bearbeiten", "edited": "Bearbeitet", "editor": "Bearbeiter", - "editor_close_without_save_prompt": "Diese Änderungen werden nicht gespeichert", + "editor_close_without_save_prompt": "Die Änderungen werden nicht gespeichert", "editor_close_without_save_title": "Editor schließen?", "editor_crop_tool_h2_aspect_ratios": "Seitenverhältnisse", "editor_crop_tool_h2_rotation": "Rotation", @@ -639,7 +639,7 @@ "incorrect_email_or_password": "Ungültige E-Mail oder Passwort", "paths_validation_failed": "{paths, plural, one {# Pfad konnte} other {# Pfade konnten}} nicht validiert werden", "profile_picture_transparent_pixels": "Profilbilder dürfen keine transparenten Pixel haben. Bitte zoome heran und/oder verschiebe das Bild.", - "quota_higher_than_disk_size": "Dein festgelegtes Kontingent ist grösser als der verfügbare Speicher", + "quota_higher_than_disk_size": "Dein festgelegtes Kontingent ist größer als der verfügbare Speicher", "repair_unable_to_check_items": "{count, select, one {Eintrag konnte} other {Einträge konnten}} nicht überprüft werden", "unable_to_add_album_users": "Benutzer konnten nicht zum Album hinzugefügt werden", "unable_to_add_assets_to_shared_link": "Datei konnte nicht zum geteilten Link hinzugefügt werden", @@ -661,7 +661,7 @@ "unable_to_complete_oauth_login": "OAuth-Anmeldung konnte nicht abgeschlossen werden", "unable_to_connect": "Verbindung konnte nicht hergestellt werden", "unable_to_connect_to_server": "Verbindung zum Server konnte nicht hergestellt werden", - "unable_to_copy_to_clipboard": "Konnte nicht in die Zwischenablage kopieren, stelle sicher, dass du per https auf die Seite zugreiffst", + "unable_to_copy_to_clipboard": "Konnte nicht in die Zwischenablage kopieren, stelle sicher, dass du per https auf die Seite zugreifst", "unable_to_create_admin_account": "Administratorkonto konnte nicht erstellt werden", "unable_to_create_api_key": "Es konnte kein API-Schlüssel erstellt werden", "unable_to_create_library": "Bibliothek konnte nicht erstellt werden", @@ -682,7 +682,7 @@ "unable_to_get_comments_number": "Anzahl der Kommentare konnte nicht abgerufen werden", "unable_to_get_shared_link": "Fehler beim Abrufen des Freigabelinks", "unable_to_hide_person": "Person kann nicht versteckt werden", - "unable_to_link_motion_video": "Bewegungsvideo kann nicht verlinkt werden", + "unable_to_link_motion_video": "Bewegungsvideo kann nicht verknüpft werden", "unable_to_link_oauth_account": "OAuth-Konto kann nicht verknüpft werden", "unable_to_load_album": "Album kann nicht geladen werden", "unable_to_load_asset_activity": "Foto-Aktivität konnte nicht geladen werden", @@ -720,10 +720,10 @@ "unable_to_scan_library": "Bibliothek konnte nicht gescannt werden", "unable_to_set_feature_photo": "Hauptfoto konnte nicht festgelegt werden", "unable_to_set_profile_picture": "Profilbild konnte nicht gesetzt werden", - "unable_to_submit_job": "Auftrag konnte nicht übermittelt werden", + "unable_to_submit_job": "Aufgabe konnte nicht eingereicht werden", "unable_to_trash_asset": "Objekte konnten nicht gelöscht werden", "unable_to_unlink_account": "Die Verknüpfung des Kontos kann nicht aufgehoben werden", - "unable_to_unlink_motion_video": "Verlinkung zum Bewegungsvideo kann nicht aufgehoben werden", + "unable_to_unlink_motion_video": "Verknüpfung zum Bewegungsvideo kann nicht aufgehoben werden", "unable_to_update_album_cover": "Album-Cover konnte nicht aktualisiert werden", "unable_to_update_album_info": "Album-Info konnte nicht aktualisiert werden", "unable_to_update_library": "Die Bibliothek konnte nicht aktualisiert werden", @@ -739,7 +739,7 @@ "every_six_hours": "Alle 6 Stunden", "exif": "EXIF", "exit_slideshow": "Diashow beenden", - "expand_all": "Alle erweitern", + "expand_all": "Alle aufklappen", "expire_after": "Verfällt nach", "expired": "Verfallen", "expires_date": "Läuft am {date} ab", @@ -771,7 +771,7 @@ "folders": "Ordner", "folders_feature_description": "Durchsuchen der Ordneransicht für Fotos und Videos im Dateisystem", "force_re-scan_library_files": "Erzwingen des erneuten Scannens aller Bibliotheksdateien", - "forward": "Weiterleiten", + "forward": "Vorwärts", "general": "Allgemein", "get_help": "Hilfe erhalten", "getting_started": "Erste Schritte", @@ -808,7 +808,7 @@ "image_taken": "{isVideo, select, true {Video aufgenommen} other {Bild aufgenommen}}", "img": "Img", "immich_logo": "Immich-Logo", - "immich_web_interface": "Immich Webschnittstelle", + "immich_web_interface": "Immich-Web-Oberfläche", "import_from_json": "Aus JSON importieren", "import_path": "Importpfad", "in_albums": "In {count, plural, one {# Album} other {# Alben}}", @@ -819,10 +819,10 @@ "individual_share": "Individuelle Freigabe", "info": "Info", "interval": { - "day_at_onepm": "Täglich 13.00 Uhr", + "day_at_onepm": "Täglich um 13:00 Uhr", "hours": "{hours, plural, one {Jede Stunde} other {Alle {hours, number} Stunden}}", "night_at_midnight": "Täglich um Mitternacht", - "night_at_twoam": "Täglich Nachts um 2.00 Uhr" + "night_at_twoam": "Täglich nachts um 2:00 Uhr" }, "invite_people": "Personen einladen", "invite_to_album": "Zum Album einladen", @@ -869,7 +869,7 @@ "license_trial_info_4": "Bitte erwäge den Kauf einer Lizenz, um die kontinuierliche Weiterentwicklung des Dienstes zu unterstützen", "light": "Hell", "like_deleted": "Like gelöscht", - "link_motion_video": "Link Bewegungsvideo", + "link_motion_video": "Bewegungsvideo verknüpfen", "link_options": "Link-Optionen", "link_to_oauth": "Link zu OAuth", "linked_oauth_account": "Verknüpftes OAuth-Konto", @@ -888,6 +888,7 @@ "look": "Erscheinungsbild", "loop_videos": "Loop-Videos", "loop_videos_description": "Aktiviere diese Option, um eine automatische Videoschleife in der Detailansicht zu erstellen.", + "main_branch_warning": "Du benutzt eine Entwicklungsversion. Wir empfehlen dringend, eine Release-Version zu verwenden!", "make": "Marke", "manage_shared_links": "Freigegebene Links verwalten", "manage_sharing_with_partners": "Gemeinsame Nutzung mit Partnern verwalten", @@ -897,8 +898,8 @@ "manage_your_devices": "Deine eingeloggten Geräte verwalten", "manage_your_oauth_connection": "Deine OAuth-Verbindung verwalten", "map": "Karte", - "map_marker_for_images": "Kartemarkierung für Bilder, die in {city}, {country} aufgenommen wurden", - "map_marker_with_image": "Kartenmarker mit Bild", + "map_marker_for_images": "Kartenmarkierung für Bilder, die in {city}, {country} aufgenommen wurden", + "map_marker_with_image": "Kartenmarkierung mit Bild", "map_settings": "Karteneinstellungen", "matches": "Treffer", "media_type": "Medientyp", @@ -939,9 +940,9 @@ "no_albums_yet": "Es sieht so aus, als hättest du noch keine Alben.", "no_archived_assets_message": "Archiviere Fotos und Videos, um sie aus deiner Fotoansicht zu entfernen", "no_assets_message": "KLICKE, UM DEIN ERSTES FOTO HOCHZULADEN", - "no_duplicates_found": "Keine Duplikate wurden gefunden.", - "no_exif_info_available": "Keine Exif-Informationen vorhanden", - "no_explore_results_message": "Lade weitere Fotos hoch, um deine Sammlung zu vergrößern.", + "no_duplicates_found": "Es wurden keine Duplikate gefunden.", + "no_exif_info_available": "Keine EXIF-Informationen vorhanden", + "no_explore_results_message": "Lade weitere Fotos hoch, um deine Sammlung zu erkunden.", "no_favorites_message": "Füge Favoriten hinzu, um deine besten Bilder und Videos schnell zu finden", "no_libraries_message": "Eine externe Bibliothek erstellen, um deine Fotos und Videos anzusehen", "no_name": "Kein Name", @@ -950,7 +951,7 @@ "no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort", "no_shared_albums_message": "Erstelle ein Album, um Fotos und Videos mit Personen in deinem Netzwerk zu teilen", "not_in_any_album": "In keinem Album", - "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um ein Storage-Label zu verwenden, starte den", + "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den", "note_unlimited_quota": "Hinweis: Verwende 0 für ein unlimitiertes Kontingent", "notes": "Notizen", "notification_toggle_setting_description": "E-Mail-Benachrichtigungen aktivieren", @@ -979,7 +980,7 @@ "organize_your_library": "Organisiere deine Bibliothek", "original": "Original", "other": "Sonstiges", - "other_devices": "Sonstige Geräte", + "other_devices": "Andere Geräte", "other_variables": "Sonstige Variablen", "owned": "Eigenes", "owner": "Besitzer", @@ -1006,14 +1007,14 @@ "pending": "Ausstehend", "people": "Personen", "people_edits_count": "{count, plural, one {# Person} other {# Personen}} bearbeitet", - "people_feature_description": "Durchsuchen von Fotos und Videos nach Personen gruppiert", + "people_feature_description": "Fotos und Videos nach Personen gruppiert durchsuchen", "people_sidebar_description": "Eine Verknüpfung zu Personen in der Seitenleiste anzeigen", "perform_library_tasks": "", "permanent_deletion_warning": "Warnung vor endgültiger Löschung", "permanent_deletion_warning_setting_description": "Anzeige einer Warnung beim permanenten Löschen von Objekten", "permanently_delete": "Dauerhaft löschen", "permanently_delete_assets_count": "{count, plural, one {Datei} other {Dateien}} dauerhaft gelöscht", - "permanently_delete_assets_prompt": "Bist du sicher, dass {count, plural, one {diese Datei} other {diese # Dateien}} dauerhaft gelöscht werden soll? Dadurch werden diese auch aus deinen Alben entfernt.", + "permanently_delete_assets_prompt": "Bist du sicher, dass {count, plural, one {diese Datei} other {diese # Dateien}} dauerhaft gelöscht werden soll? Dadurch {count, plural, one {wird} other {werden}} diese auch aus deinen Alben entfernt.", "permanently_deleted_asset": "Dauerhaft gelöschtes Objekt", "permanently_deleted_assets": "{count, plural, one {# Objekt} other {# Objekte}} dauerhaft gelöscht", "permanently_deleted_assets_count": "{count, plural, one {# Datei} other {# Dateien}} dauerhaft gelöscht", @@ -1024,13 +1025,13 @@ "photos_and_videos": "Fotos & Videos", "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", "photos_from_previous_years": "Fotos von vorherigen Jahren", - "pick_a_location": "Wählen einen Ort", + "pick_a_location": "Wähle einen Ort", "place": "Ort", "places": "Orte", "play": "Abspielen", "play_memories": "Erinnerungen abspielen", "play_motion_photo": "Bewegte Bilder abspielen", - "play_or_pause_video": "Video Abspielen oder Pausieren", + "play_or_pause_video": "Video abspielen oder pausieren", "point": "Hinweis", "port": "Port", "preset": "Voreinstellung", @@ -1043,7 +1044,7 @@ "profile_image_of_user": "Profilbild von {user}", "profile_picture_set": "Profilbild gesetzt.", "public_album": "Öffentliches Album", - "public_share": "Öffentliche Teilung", + "public_share": "Öffentliche Freigabe", "purchase_account_info": "Unterstützer", "purchase_activated_subtitle": "Danke für die Unterstützung von Immich und Open-Source Software", "purchase_activated_time": "Aktiviert am {date, date}", @@ -1060,22 +1061,22 @@ "purchase_individual_description_2": "Unterstützerstatus", "purchase_individual_title": "Einzelperson", "purchase_input_suggestion": "Besitzen Sie bereits einen Produktschlüssel? Bitte geben Sie diesen unten ein", - "purchase_license_subtitle": "Kaufe Immich um eine fortlaufende Entwicklung zu unterstützen", + "purchase_license_subtitle": "Kaufe Immich, um die fortlaufende Entwicklung zu unterstützen", "purchase_lifetime_description": "Lebenslange Gültigkeit", - "purchase_option_title": "KAUF OPTIONEN", - "purchase_panel_info_1": "Die Entwicklung von Immich erfordert viel Zeit und Mühe, und wir haben Vollzeit- Entwickler, die so gut wie möglich daran arbeiten. Unser Ziel ist es, dass Open-Source-Software und moralische Geschäftsmethoden zu einer nachhaltigen Einkommensquelle für Entwickler werden und ein datenschutzfreundliches Ökosystem mit echten Alternativen zu ausbeuterischen Cloud-Diensten geschaffen wird.", - "purchase_panel_info_2": "Weil wir davon überzeugt sind keine Paywalls zu haben, wird dieser Kauf keine zusätzlichen Funktionen in Immich freischalten. Wir verlassen uns auf Nutzende wie dich, um Entwicklung von Immich zu unterstützen.", + "purchase_option_title": "KAUFOPTIONEN", + "purchase_panel_info_1": "Die Entwicklung von Immich erfordert viel Zeit und Mühe, und wir haben Vollzeit-Entwickler, die daran arbeiten es möglichst perfekt zu machen. Unser Ziel ist es, dass Open-Source-Software und moralische Geschäftsmethoden zu einer nachhaltigen Einkommensquelle für Entwickler werden und ein datenschutzfreundliches Ökosystem mit echten Alternativen zu ausbeuterischen Cloud-Diensten geschaffen wird.", + "purchase_panel_info_2": "Weil wir davon überzeugt sind keine Paywalls zu haben, wird dieser Kauf keine zusätzlichen Funktionen in Immich freischalten. Wir verlassen uns auf Nutzende wie dich, um die Entwicklung von Immich zu unterstützen.", "purchase_panel_title": "Das Projekt unterstützen", "purchase_per_server": "Pro Server", "purchase_per_user": "Pro Benutzer", "purchase_remove_product_key": "Produktschlüssel entfernen", "purchase_remove_product_key_prompt": "Sicher, dass der Produktschlüssel entfernt werden soll?", - "purchase_remove_server_product_key": "Server Produktschlüssel entfernen", - "purchase_remove_server_product_key_prompt": "Sicher, dass der Server Produktschlüssel entfernt werden soll?", + "purchase_remove_server_product_key": "Server-Produktschlüssel entfernen", + "purchase_remove_server_product_key_prompt": "Sicher, dass der Server-Produktschlüssel entfernt werden soll?", "purchase_server_description_1": "Für den gesamten Server", "purchase_server_description_2": "Unterstützerstatus", "purchase_server_title": "Server", - "purchase_settings_server_activated": "Der Server Produktschlüssel wird durch den Administrator verwaltet", + "purchase_settings_server_activated": "Der Server-Produktschlüssel wird durch den Administrator verwaltet", "range": "Reichweite", "rating": "Bewertung", "rating_clear": "Bewertung löschen", @@ -1085,19 +1086,19 @@ "reaction_options": "Reaktionsmöglichkeiten", "read_changelog": "Changelog lesen", "reassign": "Neu zuweisen", - "reassigned_assets_to_existing_person": "{count, plural, one {# Datei} other {# Dateien}} wurden {name, select, null {einer vorhandenen Person} other {{name}}} zugewiesen", - "reassigned_assets_to_new_person": "{count, plural, one {# Datei} other {# Dateien}} wurden einer neuen Person zugewiesen", + "reassigned_assets_to_existing_person": "{count, plural, one {# Datei wurde} other {# Dateien wurden}} {name, select, null {einer vorhandenen Person} other {{name}}} zugewiesen", + "reassigned_assets_to_new_person": "{count, plural, one {# Datei wurde} other {# Dateien wurden}} einer neuen Person zugewiesen", "reassing_hint": "Markierte Dateien einer vorhandenen Person zuweisen", "recent": "Neuste", "recent_searches": "Letzte Suchen", "refresh": "Aktualisieren", - "refresh_encoded_videos": "Codierte Videos aktualisieren", + "refresh_encoded_videos": "Kodierte Videos aktualisieren", "refresh_faces": "Gesichter aktualisieren", "refresh_metadata": "Metadaten aktualisieren", "refresh_thumbnails": "Miniaturansichten aktualisieren", "refreshed": "Aktualisiert", "refreshes_every_file": "Alle bestehenden und neuen Dateien erneut einlesen", - "refreshing_encoded_video": "Codierte Videos werden aktualisiert", + "refreshing_encoded_video": "Kodierte Videos werden aktualisiert", "refreshing_faces": "Gesichter werden aktualisiert", "refreshing_metadata": "Metadaten werden aktualisiert", "regenerating_thumbnails": "Miniaturansichten werden neu erstellt", @@ -1109,12 +1110,12 @@ "remove_deleted_assets": "Offline-Dateien entfernen", "remove_from_album": "Aus Album entfernen", "remove_from_favorites": "Aus Favoriten entfernen", - "remove_from_shared_link": "Aus geteilten Link entfernen", + "remove_from_shared_link": "Aus geteiltem Link entfernen", "remove_user": "Nutzer entfernen", "removed_api_key": "API-Schlüssel {name} wurde entfernt", "removed_from_archive": "Aus dem Archiv entfernt", - "removed_from_favorites": "Von Favoriten entfernt", - "removed_from_favorites_count": "{count, plural, other {#}} von Favoriten entfernt", + "removed_from_favorites": "Aus den Favoriten entfernt", + "removed_from_favorites_count": "{count, plural, other {#}} aus den Favoriten entfernt", "removed_tagged_assets": "Tag von {count, plural, one {# Datei} other {# Dateien}} entfernt", "rename": "Umbenennen", "repair": "Reparatur", @@ -1192,8 +1193,8 @@ "send_message": "Nachricht senden", "send_welcome_email": "Begrüssungsmail senden", "server": "Server", - "server_offline": "Server Offline", - "server_online": "Server Online", + "server_offline": "Server offline", + "server_online": "Server online", "server_stats": "Server-Statistiken", "server_version": "Server-Version", "set": "Speichern", @@ -1208,7 +1209,7 @@ "shared": "Geteilt", "shared_by": "Geteilt von", "shared_by_user": "Von {user} geteilt", - "shared_by_you": "Geteilt von dir", + "shared_by_you": "Von dir geteilt", "shared_from_partner": "Fotos von {partner}", "shared_link_options": "Optionen für geteilten Link", "shared_links": "Geteilte Links", @@ -1226,7 +1227,7 @@ "show_gallery": "Galerie anzeigen", "show_hidden_people": "Ausgeblendete Personen anzeigen", "show_in_timeline": "In Zeitleiste anzeigen", - "show_in_timeline_setting_description": "Fotos und Videos dieses Benutzers in deiner Timeline anzeigen", + "show_in_timeline_setting_description": "Fotos und Videos dieses Benutzers in deiner Zeitleiste anzeigen", "show_keyboard_shortcuts": "Tastaturkürzel anzeigen", "show_metadata": "Metadaten anzeigen", "show_or_hide_info": "Informationen ein- oder ausblenden", @@ -1239,7 +1240,7 @@ "show_supporter_badge_description": "Zeige Unterstützerabzeichen", "shuffle": "Durchmischen", "sidebar": "Seitenleiste", - "sidebar_display_description": "Zeigt einen Link zu der Ansicht in der Seitenleiste an", + "sidebar_display_description": "Zeige einen Link zu der Ansicht in der Seitenleiste an", "sign_out": "Abmelden", "sign_up": "Registrieren", "size": "Größe", @@ -1247,7 +1248,7 @@ "skip_to_folders": "Springe zu Ordnern", "skip_to_tags": "Springe zu Tags", "slideshow": "Diashow", - "slideshow_settings": "Diashow Einstellungen", + "slideshow_settings": "Diashow-Einstellungen", "sort_albums_by": "Alben sortieren nach...", "sort_created": "Erstellungsdatum", "sort_items": "Anzahl der Einträge", @@ -1270,7 +1271,7 @@ "stop_photo_sharing": "Deine Fotos nicht mehr teilen?", "stop_photo_sharing_description": "{partner} wird keinen Zugriff mehr auf deine Fotos haben.", "stop_sharing_photos_with_user": "Aufhören Fotos mit diesem Benutzer zu teilen", - "storage": "Speicher", + "storage": "Speicherplatz", "storage_label": "Speicherpfad", "storage_usage": "{used} von {available} verwendet", "submit": "Bestätigen", @@ -1278,7 +1279,7 @@ "sunrise_on_the_beach": "Sonnenaufgang am Strand", "support": "Unterstützung", "support_and_feedback": "Unterstützung & Feedback", - "support_third_party_description": "Deine immich-Installation wurde von einem Drittanbieter zusammengestellt. Probleme, die bei dir auftreten, können durch dieses Paket verursacht werden. Bitte wende dich daher in erster Linie an diesen Anbieter, indem du die unten stehenden Links verwendest.", + "support_third_party_description": "Deine Immich-Installation wurde von einem Drittanbieter zusammengestellt. Probleme, die bei dir auftreten, können durch dieses Paket verursacht werden. Bitte wende dich daher in erster Linie an diesen Anbieter, indem du die unten stehenden Links verwendest.", "swap_merge_direction": "Vertauschen der Zusammenführungsrichtung", "sync": "Synchronisieren", "tag": "Tag", @@ -1309,7 +1310,7 @@ "toggle_visibility": "Sichtbarkeit umschalten", "total_usage": "Gesamtnutzung", "trash": "Papierkorb", - "trash_all": "Alles im Papierkorb", + "trash_all": "Alles in den Papierkorb", "trash_count": "Papierkorb {count, number}", "trash_delete_asset": "Datei löschen/in den Papierkorb verschieben", "trash_no_results_message": "Gelöschte Fotos und Videos werden hier angezeigt.", @@ -1324,7 +1325,7 @@ "unknown_album": "Unbekanntes Album", "unknown_year": "Unbekanntes Jahr", "unlimited": "Unlimitiert", - "unlink_motion_video": "Verlinkung zum Bewegungsvideo aufheben", + "unlink_motion_video": "Verknüpfung zum Bewegungsvideo aufheben", "unlink_oauth": "OAuth entfernen", "unlinked_oauth_account": "Nicht verknüpftes OAuth-Konto", "unnamed_album": "Unbenanntes Album", diff --git a/web/src/lib/i18n/el.json b/i18n/el.json similarity index 89% rename from web/src/lib/i18n/el.json rename to i18n/el.json index 36a4cd586d..b29e95b9c4 100644 --- a/web/src/lib/i18n/el.json +++ b/i18n/el.json @@ -54,12 +54,15 @@ "failed_job_command": "Η Εντολή {command} απέτυχε για την εργασία: {job}", "force_delete_user_warning": "ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Αυτό θα αφαιρέσει άμεσα το χρήστη και όλα τα στοιχεία. Αυτό δεν μπορεί να αναιρεθεί και τα αρχεία δεν μπορούν να ανακτηθούν.", "forcing_refresh_library_files": "Επιβολή ανανέωσης όλων των αρχείων της βιβλιοθήκης", + "image_format": "Μορφή", "image_format_description": "Η μορφή WebP παράγει μικρότερα αρχεία από τη μορφή JPEG, αλλά είναι πιο αργή στην κωδικοποίηση.", "image_prefer_embedded_preview": "Προτίμηση ενσωματωμένης προεπισκόπησης", "image_prefer_embedded_preview_setting_description": "Χρησιμοποιήστε ενσωματωμένες προεπισκοπίσεις για εικόνες RAW ως εισαγωγή στην επεξεργασία εικόνας όταν είναι διαθέσιμο. Αυτό μπορεί να δημιουργήσει πιο ακριβή χρωματα για κάποιες εικόνες, αλλά η ποιότητα των προεπισκοπίσεων εξαρτάται από την κάμερα και ενδέχεται να υπάρχουν περισσότερα μπιμπίκια λόγω συμπίεσης.", "image_prefer_wide_gamut": "Προτίμηση ευρείας γκάμας", "image_prefer_wide_gamut_setting_description": "Χρησιμοποιήστε Display P3 για τις μικρογραφίες. Αυτό διατηρεί την ζωντάνια των χρωμάτων σε εικόνες μεγάλου χρωματικού εύρους, αλλά ενδέχεται να εμφανίζονται αλλιώς σε παλαιότερες συσκευές με παλαιότερες εκδόσεις περιηγητών. Οι εικόνες sRGB μένουν ως έχουν για να αποφευχθούν χρωματικές αλλαγές.", + "image_preview_description": "Μεσαίου μεγέθους εικόνες, χωρίς μεταδεδομένα, οι οποίες χρησιμοποιύνται όταν γίνεται θέαση ενός αντικειμένου και για μηχανική μάθηση", "image_preview_format": "Μορφή προεπισκόπησης", + "image_preview_quality_description": "Ποιότητα προεπισκόπησης, απο 1-100. Μεγαλύτερες τιμές είναι καλύτερες, αλλά παράγουν μεγαλύτερα αρχεία που μπορεί να μειώσουν την ταχύτητα της εφαρμογής. Χαμηλές τιμές μπορεί να επηρεάσουν τη ποιότητα του machine learning.", "image_preview_resolution": "Ανάλυση προεπισκόπησης", "image_preview_resolution_description": "Χρησιμοποιείται κατά την προβολή μιας φωτογραφίας και για μηχανική εκμάθηση. Οι υψηλότερες αναλύσεις μπορούν να διατηρήσουν περισσότερες λεπτομέρειες, αλλά χρειάζονται περισσότερο χρόνο για την κωδικοποίηση, έχουν μεγαλύτερα μεγέθη αρχείων και μπορούν να μειώσουν την απόκριση της εφαρμογής.", "image_preview_title": "Ρυθμίσεις προεπισκόπισης", @@ -69,7 +72,9 @@ "image_resolution_description": "Υψηλότερες αναλύσεις μπορούν να διατηρήσουν περισσότερες λεπτομέρειες, αλλά χρειάζονται περισσότερο χρόνο για την κωδικοποίηση, έχουν μεγαλύτερα μεγέθη αρχείων και μπορούν να μειώσουν την απόκριση της εφαρμογής.", "image_settings": "Ρυθμίσεις Εικόνας", "image_settings_description": "Διαχείριση της ποιότητας και της ανάλυσης των εικόνων που δημιουργούνται", + "image_thumbnail_description": "Μικρό εικονίδιο χωρίς μεταδεδομένα, χρησιμοποιείται όταν γίνεται θέαση ομάδας φωτογραφιών, όπως η κύρια χρονογραμμή", "image_thumbnail_format": "Μορφή μικρογραφίας", + "image_thumbnail_quality_description": "Ποιότητα μικρογραφίας, απο 1-100. Μεγαλύτερες τιμές είναι καλύτερες, αλλά παράγουν μεγαλύτερα αρχεία που μπορεί να μειώσουν την ταχύτητα της εφαρμογής.", "image_thumbnail_resolution": "Ανάλυση μικρογραφίας", "image_thumbnail_resolution_description": "Χρησιμοποιείται κατά την προβολή ομάδων φωτογραφιών (κύριο χρονολόγιο, προβολή άλμπουμ κλπ.). Υψηλότερες αναλύσεις μπορούν να διατηρήσουν περισσότερες λεπτομέρειες, αλλά χρειάζονται περισσότερο χρόνο για την κωδικοποίηση, έχουν μεγαλύτερα μεγέθη αρχείων και μπορούν να μειώσουν την απόκριση της εφαρμογής.", "image_thumbnail_title": "Ρυθμίσεις μικρογραφίας", @@ -100,8 +105,11 @@ "logging_level_description": "Όταν είναι ενεργοποιημένο, τι επίπεδο καταγραφής να εφαρμοστεί.", "logging_settings": "Καταγραφή", "machine_learning_clip_model": "Μοντέλο CLIP", + "machine_learning_clip_model_description": "The name of a CLIP model listed here. Note that you must re-run the 'Smart Search' job for all images upon changing a model.", "machine_learning_duplicate_detection": "Εντοπισμός Διπλότυπων", "machine_learning_duplicate_detection_enabled": "Ενεργοποίηση εντοπισμού διπλότυπων", + "machine_learning_duplicate_detection_enabled_description": "Εάν απενεργοποιηθεί, θα υπάρξει και πάλι εκκαθάριση των ταυτόσημων στοιχείων.", + "machine_learning_duplicate_detection_setting_description": "Use CLIP embeddings to find likely duplicates", "machine_learning_enabled": "Ενεργοποίηση μηχανικής εκμάθησης", "machine_learning_enabled_description": "Εάν απενεργοποιηθεί, όλες οι λειτουργίες μηχανικής εκμάθησης θα απενεργοποιηθούν, ανεξάρτητα από τις παρακάτω ρυθμίσεις.", "machine_learning_facial_recognition": "Αναγνώριση προσώπου", @@ -131,6 +139,7 @@ "map_enable_description": "Ενεργοποίηση λειτουργιών χάρτη", "map_gps_settings": "Ρυθμίσεις Χάρτη & GPS", "map_gps_settings_description": "Διαχείριση Ρυθμίσεων Χάρτη & GPS (Αντίστροφη γεωκωδικοποίηση)", + "map_implications": "Η λειτουργία χάρτη βασίζεται σε εξωτερικές υπηρεσίες για τα πλακίδια (tiles.immich.cloud)", "map_light_style": "Φωτεινό Θέμα", "map_manage_reverse_geocoding_settings": "Διαχείριση ρυθμίσεων Αντίστροφης Γεωκωδικοποίησης", "map_reverse_geocoding": "Αντίστροφη Γεωκωδικοποίηση", @@ -138,8 +147,52 @@ "map_reverse_geocoding_settings": "Ρυθμίσεις Αντίστροφης Γεωκωδικοποίησης", "map_settings": "Χάρτης", "map_settings_description": "Διαχείριση ρυθμίσεων χάρτη", + "map_style_description": "URL προς αρχείο θέματος του χάρτη style.json", + "metadata_extraction_job": "Εξαγωγή μεταδεδομένων", + "metadata_extraction_job_description": "Εξαγωγή μεταδεδομένων από κάθε αρχείο, όπως τοποθεσία, πρόσωπα και ανάλυση", + "metadata_faces_import_setting": "Ενεργοποίηση εισαγωγής προσώπων", + "metadata_faces_import_setting_description": "Εισαγωγή προσώπων από EXIF εικόνων και παρόμοια αρχεία ( sidecar files)", + "metadata_settings": "Ρυθμίσεις μεταδεδομένων", + "metadata_settings_description": "Διαχείρηση ρυθμίσεων μεταδεδομένων", + "migration_job": "Μεταφορά δεδομένων (Migration)", + "migration_job_description": "Μεταφορά των εικονιδίων για αρχεία και πρόσωπα στην πιο πρόσφατη δομή αρχείων", + "no_paths_added": "Δεν προστέθηκαν διαδρομές", + "no_pattern_added": "Δεν προστέθηκε μοτίβο", + "note_apply_storage_label_previous_assets": "Σημείωση: Για να εφαρμοστεί η Ετικέτα Αποθήκευσης σε στοιχεία που είχαν αναρτηθεί παλαιότερα, εκτέλεσε το", + "note_cannot_be_changed_later": "ΣΗΜΕΊΩΣΗ: Αυτό δεν μπορεί να τροποποιηθεί αργότερα!", "note_unlimited_quota": "Σημείωση: Εισαγάγετε 0 για απεριόριστο όριο", - "notification_email_from_address": "Διεύθυνση αποστολέα" + "notification_email_from_address": "Διεύθυνση αποστολέα", + "notification_email_from_address_description": "Διεύθυνση αποστολέα, πχ: \"Immich Photo Server \"", + "notification_email_host_description": "Πάροχος του email server (πχ smtp.immich.app)", + "notification_email_ignore_certificate_errors": "Παράβλεψη των σφαλμάτων πιστοποίησης", + "notification_email_ignore_certificate_errors_description": "Παράβλεψη σφαλμάτων επικύρωσης της πιστοποίησης TLS (δεν προτείνεται)", + "notification_email_password_description": "Κωδικός για την αυθεντικοποίηση με τον server του email", + "notification_email_port_description": "Θύρα του email server (πχ 25, 465, ή 587)", + "notification_email_sent_test_email_button": "Αποστολή test email και αποθήκευση", + "notification_email_setting_description": "Ρυθμίσεις για την αποστολή ειδοποιήσεων μέσω email", + "notification_email_test_email": "Αποστολή test email", + "notification_email_test_email_failed": "Αποτυχία αποστολής test email, ελέγξτε τις ρυθμίσεις", + "notification_email_test_email_sent": "Ένα test email στάλθηκε στην διεύθυνση {email}. Παρακαλώ ελέγξτε τα εισερχόμενα σας.", + "notification_email_username_description": "Όνομα χρήστη για την αυθεντικοποίηση με τον server του email", + "notification_enable_email_notifications": "Ενεργοποίηση ειδοποιήσεων μέσω email", + "notification_settings": "Ρυθμίσεις ειδοποιήσεων", + "notification_settings_description": "Διαχείρηση ρυθμίσεων ειδοποιήσεων, συμπεριλαμβανομένου του email", + "oauth_auto_launch": "Αυτόματη εκκίνηση", + "oauth_auto_launch_description": "Αυτόματη εκκίνιση της υπηρεσίας OAuth με την πλοήγηση στην σελίδα σύνδεσης", + "oauth_auto_register": "Αυτόματη καταχώρηση", + "oauth_auto_register_description": "Αυτόματη καταχώρηση νέου χρήστη αφού συνδεθεί με OAuth", + "oauth_button_text": "Κείμενο κουμπιού", + "oauth_client_id": "Ταυτότητα πελάτη (Client)", + "oauth_client_secret": "Client Secret", + "oauth_enable_description": "Σύνδεση με OAuth", + "oauth_issuer_url": "Issuer URL", + "oauth_mobile_redirect_uri": "Mobile redirect URI", + "oauth_mobile_redirect_uri_override": "Mobile redirect URI override", + "oauth_mobile_redirect_uri_override_description": "Enable when OAuth provider does not allow a mobile URI, like '{callback}'", + "oauth_profile_signing_algorithm": "Αλγόριθμος σύνδεσης προφίλ", + "oauth_profile_signing_algorithm_description": "Αλγόριθμος που χρησιμοποιείται για την σύνδεση των χρηστών.", + "oauth_scope": "Scope", + "oauth_settings": "OAuth" }, "assets_restore_confirmation": "Είστε βέβαιοι ότι θέλετε να επαναφέρετε όλα τα στοιχεία που βρίσκονται στον κάδο απορριμμάτων; Αυτή η ενέργεια δεν μπορεί να αναιρεθεί! Λάβετε υπόψη ότι δεν θα είναι δυνατή η επαναφορά στοιχείων εκτός σύνδεσης.", "assets_restored_count": "Έγινε επαναφορά {count, plural, one {# στοιχείου} other {# στοιχείων}}", diff --git a/web/src/lib/i18n/en.json b/i18n/en.json similarity index 99% rename from web/src/lib/i18n/en.json rename to i18n/en.json index 144845ef32..1d591854f8 100644 --- a/web/src/lib/i18n/en.json +++ b/i18n/en.json @@ -816,7 +816,7 @@ "look": "Look", "loop_videos": "Loop videos", "loop_videos_description": "Enable to automatically loop a video in the detail viewer.", - "main_branch_warning": "You're running a build from the main branch. We strongly recommend using a release version!", + "main_branch_warning": "You’re using a development version; we strongly recommend using a release version!", "make": "Make", "manage_shared_links": "Manage shared links", "manage_sharing_with_partners": "Manage sharing with partners", @@ -1198,7 +1198,7 @@ "sunrise_on_the_beach": "Sunrise on the beach", "support": "Support", "support_and_feedback": "Support & Feedback", - "support_third_party_description": "Your immich installation was packaged by a third-party. Issues you experience may be caused by that package, so please raise issues with them in the first instance using the links below.", + "support_third_party_description": "Your Immich installation was packaged by a third-party. Issues you experience may be caused by that package, so please raise issues with them in the first instance using the links below.", "swap_merge_direction": "Swap merge direction", "sync": "Sync", "tag": "Tag", diff --git a/web/src/lib/i18n/es.json b/i18n/es.json similarity index 99% rename from web/src/lib/i18n/es.json rename to i18n/es.json index de46d82f25..5b26aa577b 100644 --- a/web/src/lib/i18n/es.json +++ b/i18n/es.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Aceleración por Hardware", "transcoding_hardware_acceleration_description": "Experimental; mucho más rápido, pero tendrá menor calidad con la misma tasa de bits", "transcoding_hardware_decoding": "Decodificación por hardware", - "transcoding_hardware_decoding_setting_description": "Se aplica únicamente a NVENC, QSV y RKMPP. Habilita la aceleración de un extremo a otro en lugar de solo acelerar la codificación. Puede que no funcione en todos los vídeos.", + "transcoding_hardware_decoding_setting_description": "Permite la aceleración de extremo a extremo en lugar de acelerar únicamente la codificación. Puede que no funcione en todos los vídeos.", "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Maximos B-frames", "transcoding_max_b_frames_description": "Los valores más altos mejoran la eficiencia de la compresión, pero ralentizan la codificación. Puede que no sea compatible con la aceleración de hardware en dispositivos más antiguos. 0 desactiva los fotogramas B, mientras que -1 establece este valor automáticamente.", @@ -888,6 +888,7 @@ "look": "Mirar", "loop_videos": "Vídeos en bucle", "loop_videos_description": "Habilite la reproducción automática de un video en el visor de detalles.", + "main_branch_warning": "Estás ejecutando una compilación desde la rama principal. ¡Recomendamos encarecidamente usar una versión de lanzamiento!", "make": "Marca", "manage_shared_links": "Administrar enlaces compartidos", "manage_sharing_with_partners": "Administrar el uso compartido con invitados", diff --git a/web/src/lib/i18n/et.json b/i18n/et.json similarity index 98% rename from web/src/lib/i18n/et.json rename to i18n/et.json index 80d37c206e..074ddb9f8b 100644 --- a/web/src/lib/i18n/et.json +++ b/i18n/et.json @@ -283,7 +283,7 @@ "transcoding_hardware_acceleration": "Riistvaraline kiirendus", "transcoding_hardware_acceleration_description": "Eksperimentaalne; palju kiirem, aga sama bitisageduse juures madalam kvaliteet", "transcoding_hardware_decoding": "Riistvaraline dekodeerimine", - "transcoding_hardware_decoding_setting_description": "Rakendub ainult NVENC, QSV ja RKMPP puhul. Võimaldab protsessi läbivalt kiirendada, mitte ainult kodeerimist. Ei pruugi kõigi videote puhul töötada.", + "transcoding_hardware_decoding_setting_description": "Võimaldab protsessi läbivalt kiirendada, mitte ainult kodeerimist. Ei pruugi kõigi videote puhul töötada.", "transcoding_hevc_codec": "HEVC koodek", "transcoding_max_b_frames": "Maksimaalne B-kaadrite arv", "transcoding_max_b_frames_description": "Kõrgemad väärtused parandavad pakkimise efektiivsust, aga aeglustavad kodeerimist. See valik ei pruugi olla ühilduv riistvaralise kiirendusega vanematel seadmetel. 0 lülitab B-kaadrid välja, -1 määrab väärtuse automaatselt.", @@ -705,6 +705,7 @@ "unable_to_update_library": "Kogu uuendamine ebaõnnestus", "unable_to_update_location": "Asukoha muutmine ebaõnnestus", "unable_to_update_settings": "Seadete muutmine ebaõnnestus", + "unable_to_update_timeline_display_status": "Ajajoonel kuvamise uuendamine ebaõnnestus", "unable_to_update_user": "Kasutaja muutmine ebaõnnestus", "unable_to_upload_file": "Faili üleslaadimine ebaõnnestus" }, @@ -820,6 +821,7 @@ "look": "Välimus", "loop_videos": "Taasesita videod", "loop_videos_description": "Lülita sisse, et detailvaates videot automaatselt taasesitada.", + "main_branch_warning": "Sa kasutad arendusversiooni; soovitame tungivalt kasutada väljalaskeversiooni!", "make": "Mark", "manage_shared_links": "Halda jagatud linke", "manage_sharing_with_partners": "Halda partneritega jagamist", @@ -832,6 +834,7 @@ "map_marker_for_images": "Kaardimarker kohas {city}, {country} tehtud piltide jaoks", "map_marker_with_image": "Kaardimarker pildiga", "map_settings": "Kaardi seaded", + "matches": "Ühtivad failid", "media_type": "Meedia tüüp", "memories": "Mälestused", "memories_setting_description": "Halda, mida sa oma mälestustes näed", @@ -874,6 +877,8 @@ "no_explore_results_message": "Oma kogu avastamiseks laadi üles rohkem fotosid.", "no_favorites_message": "Lisa lemmikud, et oma parimaid fotosid ja videosid kiiresti leida", "no_libraries_message": "Lisa väline kogu oma fotode ja videote vaatamiseks", + "no_name": "Nimetu", + "no_places": "Kohti ei ole", "no_results": "Vasteid pole", "no_results_description": "Proovi sünonüümi või üldisemat märksõna", "no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada", @@ -894,6 +899,7 @@ "onboarding": "Kasutuselevõtt", "onboarding_privacy_description": "Järgnevad (valikulised) funktsioonid sõltuvad välistest teenustest ning neid saab igal ajal administraatori seadetes välja lülitada.", "onboarding_theme_description": "Vali oma serverile värviteema. Saad seda hiljem seadetes muuta.", + "onboarding_welcome_description": "Algväärtustame mõned levinumad seaded.", "onboarding_welcome_user": "Tere tulemast, {user}", "online": "Ühendatud", "only_favorites": "Ainult lemmikud", @@ -962,6 +968,7 @@ "previous": "Eelmine", "previous_memory": "Eelmine mälestus", "previous_or_next_photo": "Eelmine või järgmine foto", + "primary": "Peamine", "privacy": "Privaatsus", "profile_image_of_user": "Kasutaja {user} profiilipilt", "profile_picture_set": "Profiilipilt määratud.", @@ -1051,8 +1058,11 @@ "restore_user": "Taasta kasutaja", "restored_asset": "Üksus taastatud", "resume": "Jätka", + "retry_upload": "Proovi üleslaadimist uuesti", "review_duplicates": "Vaata duplikaadid läbi", "role": "Roll", + "role_editor": "Muutja", + "role_viewer": "Vaataja", "save": "Salvesta", "saved_api_key": "API võti salvestatud", "saved_profile": "Profiil salvestatud", @@ -1171,6 +1181,7 @@ "stack_selected_photos": "Virnasta valitud fotod", "stacked_assets_count": "{count, plural, one {# üksus} other {# üksust}} virnastatud", "stacktrace": "Pinujälg", + "start": "Alusta", "start_date": "Alguskuupäev", "state": "Osariik", "status": "Staatus", @@ -1183,8 +1194,10 @@ "storage_usage": "{used}/{available} kasutatud", "suggestions": "Soovitused", "sunrise_on_the_beach": "Päikesetõus rannal", + "support": "Tugi", "support_and_feedback": "Tugi ja tagasiside", "support_third_party_description": "Sinu Immich'i install on kolmanda osapoole pakendatud. Probleemid, mida täheldad, võivad olla põhjustatud selle pakendamise poolt, seega võta esmajärjekorras nendega ühendust, kasutades allolevaid linke.", + "swap_merge_direction": "Muuda ühendamise suunda", "sync": "Sünkrooni", "tag": "Silt", "tag_assets": "Sildista üksuseid", @@ -1198,6 +1211,7 @@ "theme": "Teema", "theme_selection": "Teema valik", "theme_selection_description": "Sea automaatselt hele või tume teema vastavalt veebilehitseja eelistustele", + "they_will_be_merged_together": "Nad ühendatakse kokku", "third_party_resources": "Kolmanda osapoole ressursid", "time_based_memories": "Ajapõhised mälestused", "timezone": "Ajavöönd", @@ -1209,6 +1223,7 @@ "toggle_theme": "Lülita tume teema", "total_usage": "Kogukasutus", "trash": "Prügikast", + "trash_all": "Kõik prügikasti", "trash_count": "Liiguta {count, number} prügikasti", "trash_delete_asset": "Kustuta üksus", "trash_no_results_message": "Siia ilmuvad prügikasti liigutatud fotod ja videod.", @@ -1216,6 +1231,7 @@ "type": "Tüüp", "unarchive": "Taasta arhiivist", "unarchived_count": "{count, plural, other {# arhiivist taastatud}}", + "unfavorite": "Eemalda lemmikutest", "unhide_person": "Ära peida isikut", "unknown": "Teadmata", "unknown_year": "Teadmata aasta", @@ -1229,10 +1245,12 @@ "unstacked_assets_count": "{count, plural, one {# üksus} other {# üksust}} eraldatud", "untracked_files": "Mittejälgitavad failid", "untracked_files_decription": "Rakendus ei jälgi neid faile. Need võivad olla põhjustatud ebaõnnestunud liigutamisest, katkestatud üleslaadimisest või rakenduse veast", + "up_next": "Järgmine", "updated_password": "Parool muudetud", "upload": "Laadi üles", "upload_concurrency": "Üleslaadimise samaaegsus", "upload_errors": "Üleslaadimine lõpetatud {count, plural, one {# veaga} other {# veaga}}, uute üksuste nägemiseks värskenda lehte.", + "upload_progress": "Ootel {remaining, number} - Töödeldud {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# dubleeritud üksus} other {# dubleeritud üksust}} vahele jäetud", "upload_status_duplicates": "Duplikaadid", "upload_status_errors": "Vead", diff --git a/web/src/lib/i18n/fa.json b/i18n/fa.json similarity index 100% rename from web/src/lib/i18n/fa.json rename to i18n/fa.json diff --git a/web/src/lib/i18n/fi.json b/i18n/fi.json similarity index 99% rename from web/src/lib/i18n/fi.json rename to i18n/fi.json index 93522e4aeb..bb5a60f49f 100644 --- a/web/src/lib/i18n/fi.json +++ b/i18n/fi.json @@ -243,7 +243,7 @@ "storage_template_hash_verification_enabled": "Tarkistussumman varmennus käytössä", "storage_template_hash_verification_enabled_description": "Ottaa käyttöön varmistussummien laskennan. Älä poista käytöstä jollet ole aivan varma seurauksista", "storage_template_migration": "Tallennustilan mallien migraatio", - "storage_template_migration_description": "Käytä nykyistä {template}:a aikaisemmin lähetettyihin kohteisiin", + "storage_template_migration_description": "Käytä nykyistä {template}a aikaisemmin lähetettyihin kohteisiin", "storage_template_migration_info": "Malli vaikuttaa vain uusiin kohteisiin. Käyttääksesi mallia jo olemassa oleviin kohteisiin, aja {job}.", "storage_template_migration_job": "Tallennustilan mallin muutostyö", "storage_template_more_details": "Saadaksesi lisätietoa tästä ominaisuudesta, katso Tallennustilan Mallit sekä mihin se vaikuttaa", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Laitteistokiihdytys", "transcoding_hardware_acceleration_description": "Kokeellinen. Paljon nopeampi, mutta huonompaa laatua samalla bittinopeudella", "transcoding_hardware_decoding": "Laitteiston dekoodaus", - "transcoding_hardware_decoding_setting_description": "Vaikuttaa vain NVENC ja RKMPP -moottoreihin. Ottaa käyttöön end-to-end kiihdytyksen pelkän muuntamisen sijasta. Ei välttämättä toimi kaikissa videoissa.", + "transcoding_hardware_decoding_setting_description": "Ottaa käyttöön end-to-end kiihdytyksen pelkän muuntamisen sijasta. Ei välttämättä toimi kaikissa videoissa.", "transcoding_hevc_codec": "HEVC koodekki", "transcoding_max_b_frames": "B-kehysten enimmäismäärä", "transcoding_max_b_frames_description": "Korkeampi arvo parantaa pakkausta, mutta hidastaa enkoodausta. Ei välttämättä ole yhteensopiva vanhempien laitteiden kanssa. 0 poistaa B-kehykset käytöstä, -1 määrittää arvon automaattisesti.", @@ -432,8 +432,8 @@ "birthdate_set_description": "Syntymäaikaa käytetään laskemaan henkilön ikä kuvanottohetkellä.", "blurred_background": "Sumennettu tausta", "bugs_and_feature_requests": "Bugit ja ominaisuuspyynnöt", - "build": "Rakenna", - "build_image": "Rakenna kuva", + "build": "Koontiversio", + "build_image": "Koontiversion kuva", "bulk_delete_duplicates_confirmation": "Haluatko varmasti poistaa {count, plural, one {# kaksoiskappaleen} other {# kaksoiskappaleet}} kerralla? Tämä säilyttää kustakin mediasta kookkaimman ja poistaa loput pysyvästi. Et voi perua tätä!", "bulk_keep_duplicates_confirmation": "Haluatko varmasti säilyttää {count, plural, one {# kaksoiskappaleen} other {# kaksoiskappaleet}}? Tämä merkitsee kaikki kaksoiskappaleet ratkaistuiksi, eikä poista mitään.", "bulk_trash_duplicates_confirmation": "Haluatko varmasti siirtää {count, plural, one {# kaksoiskappaleen} other {# kaksoiskappaleet}} roskakoriin? Tämä säilyttää kustakin mediasta kookkaimman ja siirtää loput roskakoriin.", @@ -863,6 +863,7 @@ "look": "Tyyli", "loop_videos": "Toista videot uudelleen", "loop_videos_description": "Ota käyttöön videon automaattinen toisto tarkemmassa näkymässä.", + "main_branch_warning": "Käytät kehitysversiota; suosittelemme vahvasti käyttämään julkaisuversiota!", "make": "Valmistaja", "manage_shared_links": "Hallitse jaettuja linkkejä", "manage_sharing_with_partners": "Hallitse jakamista kumppaneille", @@ -1339,7 +1340,7 @@ "version": "Versio", "version_announcement_closing": "Ystäväsi Alex", "version_announcement_message": "Hei! Sovelluksen uusi versio on saatavilla. Käythän vilkaisemassa julkaisun tiedot ja varmistathan, että docker-compose.yml ja .env määritykset ovat ajan tasalla. Näin varmistat järjestelmän toimivuuden, varsinkin jos käytät WatchToweria tai muuta automaattista päivitysjärjestelmää.", - "version_history": "Versio historia", + "version_history": "Versiohistoria", "version_history_item": "Asennettu {version} päivänä {date}", "video": "Video", "video_hover_setting": "Toista esikatselun video kun kursori viedään sen päälle", diff --git a/web/src/lib/i18n/fr.json b/i18n/fr.json similarity index 99% rename from web/src/lib/i18n/fr.json rename to i18n/fr.json index 1787fae24e..781f801bb9 100644 --- a/web/src/lib/i18n/fr.json +++ b/i18n/fr.json @@ -61,7 +61,7 @@ "image_prefer_embedded_preview": "Préférer l'aperçu intégré", "image_prefer_embedded_preview_setting_description": "Utiliser les miniatures intégrées dans les photos au format RAW comme entrées pour le traitement d'image quand elles sont disponibles. Cela peut donner des couleurs plus justes pour certaines images, mais la qualité des miniatures est dépendant de l'appareil photo et l'image peut avoir des artéfacts de compression.", "image_prefer_wide_gamut": "Préférer une gamme de couleurs étendue", - "image_prefer_wide_gamut_setting_description": "Utiliser Display P3 pour les miniatures. Cela préserve mieux la vibrance des images avec des espaces colorimétriques étendus, mais les images peuvent apparaître différemment sur les anciens appareils avec une ancienne version du navigateur. Conserver les images sRGB en sRGB pour éviter les décalages de couleur.", + "image_prefer_wide_gamut_setting_description": "Utiliser Display P3 pour les miniatures. Cela préserve mieux la vivacité des images avec des espaces colorimétriques étendus, mais les images peuvent apparaître différemment sur les anciens appareils avec une ancienne version du navigateur. Conserver les images sRGB en sRGB pour éviter les décalages de couleur.", "image_preview_description": "Image de taille moyenne avec métadonnées retirées, utilisée lors de la visualisation d'un seul média et pour l'apprentissage automatique", "image_preview_format": "Format des aperçus", "image_preview_quality_description": "Qualité de l'aperçu : de 1 à 100. Une valeur plus élevée produit de meilleurs résultats, mais elle produit des fichiers plus volumineux et peut réduire la réactivité de l'application. Une valeur trop basse peut affecter la qualité de l'apprentissage automatique.", @@ -91,7 +91,7 @@ "library_created": "Bibliothèque créée : {library}", "library_cron_expression": "Expression Cron", "library_cron_expression_description": "Réglez l'intervalle d'analyse en utilisant le format cron. Pour plus d'informations, veuillez consulter par exemple Crontab Guru", - "library_cron_expression_presets": "Expressions Cron enregistrées", + "library_cron_expression_presets": "Préréglages d'expressions Cron", "library_deleted": "Bibliothèque supprimée", "library_import_path_description": "Spécifier un dossier à importer. Ce dossier, y compris les sous-dossiers, sera analysé à la recherche d'images et de vidéos.", "library_scanning": "Analyse périodique", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Accélération matérielle", "transcoding_hardware_acceleration_description": "Expérimental ; beaucoup plus rapide, mais aura une qualité inférieure pour un même débit binaire", "transcoding_hardware_decoding": "Décodage matériel", - "transcoding_hardware_decoding_setting_description": "S'applique uniquement à NVENC, QSV et RKMPP. Active l'accélération de bout en bout au lieu d'accélérer uniquement l'encodage. Peut ne pas fonctionner sur toutes les vidéos.", + "transcoding_hardware_decoding_setting_description": "Active l'accélération de bout en bout au lieu d'accélérer uniquement l'encodage. Peut ne pas fonctionner sur toutes les vidéos.", "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Nombre maximum de trames B", "transcoding_max_b_frames_description": "Des valeurs plus élevées améliorent l'efficacité de la compression, mais ralentissent l'encodage. Elles peuvent ne pas être compatibles avec l'accélération matérielle sur les anciens appareils. Une valeur de 0 désactive les trames B, tandis qu'une valeur de -1 définit automatiquement ce paramètre.", @@ -749,7 +749,7 @@ "export_as_json": "Exporter en JSON", "extension": "Extension", "external": "Externe", - "external_libraries": "Bibliothèques externes", + "external_libraries": "Bibliothèques ext.", "face_unassigned": "Non attribué", "failed_to_get_people": "Impossible d'obtenir les personnes", "favorite": "Favori", @@ -888,6 +888,7 @@ "look": "Regarder", "loop_videos": "Vidéos en boucle", "loop_videos_description": "Activer pour voir la vidéo en boucle dans le lecteur détaillé.", + "main_branch_warning": "Vous utilisez une version de développement. Nous vous recommandons fortement d'utiliser une version stable !", "make": "Marque", "manage_shared_links": "Gérer les liens partagés", "manage_sharing_with_partners": "Gérer le partage avec les partenaires", diff --git a/web/src/lib/i18n/he.json b/i18n/he.json similarity index 99% rename from web/src/lib/i18n/he.json rename to i18n/he.json index 9f457c6e10..7cb896f1f0 100644 --- a/web/src/lib/i18n/he.json +++ b/i18n/he.json @@ -29,9 +29,9 @@ "admin": { "add_exclusion_pattern_description": "הוסף דפוסי החרגה. נתמכת התאמת דפוסים באמצעות *, ** ו-?. כדי להתעלם מכל הקבצים בתיקיה כלשהי בשם \"Raw\", השתמש ב \"**/Raw/**\". כדי להתעלם מכל הקבצים המסתיימים ב \"tif.\", השתמש ב \"tif.*/**\". כדי להתעלם מנתיב מוחלט, השתמש ב \"**/נתיב/להתעלמות\".", "asset_offline_description": "נכס ספרייה חיצונית זה לא נמצא יותר בדיסק והועבר לאשפה. אם הקובץ הועבר מתוך הספרייה, בדוק את ציר הזמן שלך עבור הנכס המקביל החדש. כדי לשחזר נכס זה, נא לוודא ש-Immich יכול לגשת אל נתיב הקובץ למטה וסרוק מחדש את הספרייה.", - "authentication_settings": "הגדרות אימות", - "authentication_settings_description": "נהל סיסמה, OAuth, והגדרות אימות אחרות", - "authentication_settings_disable_all": "האם את/ה בטוח/ה שברצונך להשבית את כל שיטות ההתחברות? כניסה למערכת תהיה מושבתת לחלוטין.", + "authentication_settings": "הגדרות התחברות", + "authentication_settings_description": "נהל סיסמה, OAuth, והגדרות התחברות אחרות", + "authentication_settings_disable_all": "האם ברצונך להשבית את כל שיטות ההתחברות? כניסה למערכת תהיה מושבתת לחלוטין.", "authentication_settings_reenable": "כדי לאפשר מחדש, השתמש בפקודת שרת.", "background_task_job": "משימות רקע", "check_all": "סמן הכל", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "האצת חומרה", "transcoding_hardware_acceleration_description": "ניסיוני; המרה הרבה יותר מהירה, אבל תהיה באיכות נמוכה יותר באותו קצב סיביות", "transcoding_hardware_decoding": "פענוח חומרה", - "transcoding_hardware_decoding_setting_description": "חל רק על NVENC, QSV ו-RKMPP. מאפשר האצה מקצה לקצה במקום רק להאיץ קידוד. ייתכן שלא יפעל על כל הסרטונים.", + "transcoding_hardware_decoding_setting_description": "מאפשר האצה מקצה לקצה במקום רק האצת קידוד. ייתכן שלא יפעל על כל הסרטונים.", "transcoding_hevc_codec": "קידוד HEVC", "transcoding_max_b_frames": "B-פריימים מרביים", "transcoding_max_b_frames_description": "ערכים גבוהים יותר משפרים את יעילות הדחיסה, אך מאטים את הקידוד. ייתכן שלא יהיה תואם עם האצת חומרה במכשירים ישנים יותר. 0 משבית את B-פריימים, בעוד ש1- מגדיר את הערך זה באופן אוטומטי.", @@ -888,6 +888,7 @@ "look": "מראה", "loop_videos": "הפעלה חוזרת של סרטונים", "loop_videos_description": "אפשר הפעלה חוזרת אוטומטית של סרטון במציג הפרטים.", + "main_branch_warning": "את/ה משתמש/ת בגרסת פיתוח; אנחנו ממליצים בחום להשתמש בגרסה יציבה!", "make": "תוצרת", "manage_shared_links": "נהל קישורים משותפים", "manage_sharing_with_partners": "נהל שיתוף עם שותפים", @@ -1395,5 +1396,5 @@ "years_ago": "לפני {years, plural, one {שנה #} other {# שנים}}", "yes": "כן", "you_dont_have_any_shared_links": "אין לך קישורים משותפים", - "zoom_image": "התקרב לתמונה" + "zoom_image": "זום לתמונה" } diff --git a/web/src/lib/i18n/hi.json b/i18n/hi.json similarity index 100% rename from web/src/lib/i18n/hi.json rename to i18n/hi.json diff --git a/web/src/lib/i18n/hr.json b/i18n/hr.json similarity index 100% rename from web/src/lib/i18n/hr.json rename to i18n/hr.json diff --git a/web/src/lib/i18n/hu.json b/i18n/hu.json similarity index 99% rename from web/src/lib/i18n/hu.json rename to i18n/hu.json index 81142e6703..4bde75b5ef 100644 --- a/web/src/lib/i18n/hu.json +++ b/i18n/hu.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Hardveres Gyorsítás", "transcoding_hardware_acceleration_description": "Kísérleti funkció. Sokkal gyorsabb, viszont azonos bitrátán is alacsonyabb minőséghez vezet", "transcoding_hardware_decoding": "Hardveres dekódolás", - "transcoding_hardware_decoding_setting_description": "Csak NVENC, QSV és RKMPP esetén. Lehetővé teszi az egész folyamat gyorsítását ahelyett, hogy csak az átkódolást gyorsítaná. Nem biztos, hogy minden videó esetén működik.", + "transcoding_hardware_decoding_setting_description": "Lehetővé teszi az egész folyamat gyorsítását a pusztán kódolás gyorsítása helyett. Nem biztos, hogy minden videó esetén működik.", "transcoding_hevc_codec": "HEVC kodek", "transcoding_max_b_frames": "B-képkockák maximum száma", "transcoding_max_b_frames_description": "Nagyobb értékek megnövelik a tömörítés hatékonyságát, de lelassítják a kódolást. Nem minden hardvereszköz támogatja. A 0 érték kikapcsolja a B-képkockákat, míg -1 esetén a szoftver magának választ értéket.", @@ -860,6 +860,7 @@ "look": "Megjelenítés", "loop_videos": "Videók ismétlése", "loop_videos_description": "Engedélyezi a videók folyamatosan ismételt lejátszását.", + "main_branch_warning": "Fejlesztői verziót használsz. Javasoljuk a stabil verzió használatát!", "make": "Gyártó", "manage_shared_links": "Megosztási linkek kezelése", "manage_sharing_with_partners": "Partnerekkel való megosztás kezelése", @@ -1110,7 +1111,7 @@ "review_duplicates": "Duplikátumok áttekintése", "role": "Jogkör", "role_editor": "Szerkesztő", - "role_viewer": "Néző", + "role_viewer": "Megjelenítő", "save": "Mentés", "saved_api_key": "API Kulcs Elmentve", "saved_profile": "Profil elmentve", @@ -1268,7 +1269,7 @@ "third_party_resources": "Harmadik Féltől Származó Források", "time_based_memories": "Emlékek idő alapján", "timezone": "Időzóna", - "to_archive": "Archívum", + "to_archive": "Archiválás", "to_change_password": "Jelszó megváltoztatása", "to_favorite": "Kedvenc", "to_login": "Bejelentkezés", @@ -1335,7 +1336,7 @@ "variables": "Változók", "version": "Verzió", "version_announcement_closing": "Barátsággal, Alex", - "version_announcement_message": "Szia barátom, az alkalmazásnak van egy új verziója. Kérjük, szánj időt a kiadási megjegyzések áttekintésére, és győződj meg róla, hogy a docker-compose.yml és a .env beállításaid naprakészek, hogy elkerüld a hibás konfigurációkat, különösen, ha a WatchTower-t vagy bármilyen automatikus frissítési megoldást használsz.", + "version_announcement_message": "Szia barátom, az alkalmazásnak van egy új verziója. Kérjük, szánj időt a kiadási megjegyzések áttekintésére, és győződj meg róla, hogy a docker-compose.yml és az .env beállításaid naprakészek, hogy elkerüld a hibás konfigurációkat, különösen, ha a WatchTower-t vagy bármilyen automatikus frissítési megoldást használsz.", "version_history": "Verziótörténet", "version_history_item": "{version} telepítve: {date}", "video": "Videó", diff --git a/web/src/lib/i18n/hy.json b/i18n/hy.json similarity index 100% rename from web/src/lib/i18n/hy.json rename to i18n/hy.json diff --git a/web/src/lib/i18n/id.json b/i18n/id.json similarity index 99% rename from web/src/lib/i18n/id.json rename to i18n/id.json index 6112eb99cf..b95d02d28d 100644 --- a/web/src/lib/i18n/id.json +++ b/i18n/id.json @@ -284,7 +284,7 @@ "transcoding_hardware_acceleration": "Akselerasi Perangkat Keras", "transcoding_hardware_acceleration_description": "Uji coba; lebih cepat, tetapi akan memiliki kualitas lebih rendah pada kecepatan bit yang sama", "transcoding_hardware_decoding": "Dekode perangkat keras", - "transcoding_hardware_decoding_setting_description": "Hanya diterapkan pada NVENC dan RKMPP. Mengaktifkan akselerasi ujung ke ujung daripada hanya mengakselerasi pengodean. Mungkin tidak berfungsi pada semua video.", + "transcoding_hardware_decoding_setting_description": "Mengaktifkan akselerasi ujung ke ujung daripada hanya mengakselerasi pengodean. Mungkin tidak berfungsi pada semua video.", "transcoding_hevc_codec": "Kodek HEVC", "transcoding_max_b_frames": "Bingkai B maksimum", "transcoding_max_b_frames_description": "Nilai yang lebih tinggi meningkatkan efisiensi kompresi, tetapi membuat pengodean lebih lambat. Mungkin tidak kompatibel dengan akselerasi perangkat keras pada perangkat lawas. 0 menonaktifkan bingkai B, sedangkan -1 mengatur nilai ini secara otomatis.", @@ -863,6 +863,7 @@ "look": "Tampilan", "loop_videos": "Ulangi video", "loop_videos_description": "Aktifkan untuk mengulangi video secara otomatis dalam penampil detail.", + "main_branch_warning": "Anda menggunakan versi pengembangan; kami sangat menyarankan menggunakan versi rilis!", "make": "Merek", "manage_shared_links": "Kelola tautan terbagi", "manage_sharing_with_partners": "Kelola pembagian dengan partner", diff --git a/web/src/lib/i18n/it.json b/i18n/it.json similarity index 99% rename from web/src/lib/i18n/it.json rename to i18n/it.json index 77f79021a0..12821fd654 100644 --- a/web/src/lib/i18n/it.json +++ b/i18n/it.json @@ -2,7 +2,7 @@ "about": "Informazioni", "account": "Profilo", "account_settings": "Impostazioni Account", - "acknowledge": "Ho capito", + "acknowledge": "Acconsento", "action": "Azione", "actions": "Azioni", "active": "Attivi", @@ -51,8 +51,8 @@ "external_library_created_at": "Libreria esterna (creata il {date})", "external_library_management": "Gestione Librerie Esterne", "face_detection": "Rilevamento Volti", - "face_detection_description": "Rileva i volti presenti negli assets utilizzando il machine learning. Per i video, viene presa in considerazione solo la miniatura. \"Tutto\" (ri-)processerà tutti gli assets. \"Mancanti\" seleziona solo gli assets che non sono ancora stati processati. I volti rilevati verranno selezionati per il riconoscimento facciale dopo che il rilevamento dei volti sarà stato completato, raggruppandoli in persone esistenti e/o nuove.", - "facial_recognition_job_description": "Raggruppa i volti rilevati in persone. Questo processo viene eseguito dopo che il rilevamento volti è stato completato. \"Tutti\" (ri-)unisce tutti i volti. \"Mancanti\" processa i volti che non hanno una persona assegnata.", + "face_detection_description": "Rileva i volti presenti negli assets utilizzando il machine learning. Per i video, viene presa in considerazione solo la miniatura. \"Aggiorna\" (ri-)processerà tutti gli assets. \"Reset\" inoltre elimina tutti i dati dei volti correnti. \"Mancanti\" seleziona solo gli assets che non sono ancora stati processati. I volti rilevati verranno selezionati per il riconoscimento facciale dopo che il rilevamento dei volti sarà stato completato, raggruppandoli in persone esistenti e/o nuove.", + "facial_recognition_job_description": "Raggruppa i volti rilevati in persone. Questo processo viene eseguito dopo che il rilevamento volti è stato completato. \"Reset\" (ri-)unisce tutti i volti. \"Mancanti\" processa i volti che non hanno una persona assegnata.", "failed_job_command": "Il comando {command} è fallito per il processo: {job}", "force_delete_user_warning": "ATTENZIONE: Questo rimuoverà immediatamente l'utente e tutti i suoi assets. Non è possibile tornare indietro e i file non potranno essere recuperati.", "forcing_refresh_library_files": "Forzando l'aggiornamento completo della libreria", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Accelerazione Hardware", "transcoding_hardware_acceleration_description": "Sperimentale; molto più veloce, ma avrà una qualità inferiore allo stesso bitrate", "transcoding_hardware_decoding": "Decodifica hardware", - "transcoding_hardware_decoding_setting_description": "Si applica solo a NVENC, QSV e RKMPP. Abilita l'accelerazione end-to-end anziché solo l'accelerazione dell'encoding. Potrebbe non funzionare su tutti i video.", + "transcoding_hardware_decoding_setting_description": "Abilita l'accelerazione end-to-end anziché accelerare solo la codifica. Potrebbe non funzionare su tutti i video.", "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "B-frames Massimi", "transcoding_max_b_frames_description": "Valori più alti migliorano l'efficienza di compressione, ma rallentano l'encoding. Potrebbero non essere compatibili con l'accelerazione hardware su dispositivi più vecchi. 0 disabilita i B-frames, mentre -1 imposta questo valore automaticamente.", @@ -888,6 +888,7 @@ "look": "Guarda", "loop_videos": "Riproduci video in loop", "loop_videos_description": "Abilita per riprodurre automaticamente un video in loop nella vista dettagli.", + "main_branch_warning": "Stai usando una versione di sviluppo. Consigliamo vivamente di utilizzare una versione di rilascio!", "make": "Produttore", "manage_shared_links": "Gestisci link condivisi", "manage_sharing_with_partners": "Gestisci la condivisione con i compagni", @@ -1091,13 +1092,13 @@ "recent_searches": "Ricerche recenti", "refresh": "Aggiorna", "refresh_encoded_videos": "Ricarica video codificati", - "refresh_faces": "Aggiorna faccie", + "refresh_faces": "Aggiorna facce", "refresh_metadata": "Ricarica metadati", "refresh_thumbnails": "Ricarica anteprime", "refreshed": "Aggiornato", "refreshes_every_file": "Rilegge tutti i file esistenti e nuovi", "refreshing_encoded_video": "Ricaricando il video codificato", - "refreshing_faces": "Aggiorna Faccie", + "refreshing_faces": "Aggiorna Facce", "refreshing_metadata": "Ricaricando i metadati", "regenerating_thumbnails": "Rigenerando le anteprime", "remove": "Rimuovi", diff --git a/web/src/lib/i18n/ja.json b/i18n/ja.json similarity index 100% rename from web/src/lib/i18n/ja.json rename to i18n/ja.json diff --git a/web/src/lib/i18n/kmr.json b/i18n/kmr.json similarity index 100% rename from web/src/lib/i18n/kmr.json rename to i18n/kmr.json diff --git a/web/src/lib/i18n/ko.json b/i18n/ko.json similarity index 99% rename from web/src/lib/i18n/ko.json rename to i18n/ko.json index 210b614ffa..5ec2a984d5 100644 --- a/web/src/lib/i18n/ko.json +++ b/i18n/ko.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "하드웨어 가속", "transcoding_hardware_acceleration_description": "실험적인 기능입니다. 속도가 향상되지만 동일 비트레이트에서 품질이 상대적으로 낮을 수 있습니다.", "transcoding_hardware_decoding": "하드웨어 디코딩", - "transcoding_hardware_decoding_setting_description": "인코딩 가속을 위해 엔드 투 엔드 가속을 사용합니다. 모든 동영상에서 작동하지 않을 수 있습니다. (NVENC, QSV 및 RKMPP만 해당)", + "transcoding_hardware_decoding_setting_description": "인코딩 가속을 위해 엔드 투 엔드 가속을 사용합니다. 모든 동영상에서 작동하지 않을 수 있습니다.", "transcoding_hevc_codec": "HEVC 코덱", "transcoding_max_b_frames": "최대 B 프레임", "transcoding_max_b_frames_description": "값이 높으면 압축 효율이 향상되지만 인코딩 속도가 저하됩니다. 오래된 기기의 하드웨어 가속과 호환되지 않을 수 있습니다. 0을 입력한 경우 B 프레임을 비활성화하며, -1을 입력한 경우 자동으로 설정합니다.", @@ -682,6 +682,7 @@ "unable_to_get_comments_number": "댓글의 개수를 불러올 수 없습니다.", "unable_to_get_shared_link": "공유 링크를 불러오지 못했습니다.", "unable_to_hide_person": "인물을 숨길 수 없습니다.", + "unable_to_link_motion_video": "모션 비디오를 연결할 수 없습니다", "unable_to_link_oauth_account": "OAuth 계정을 연결할 수 없습니다.", "unable_to_load_album": "앨범을 불러올 수 없습니다.", "unable_to_load_asset_activity": "사용자의 반응을 불러올 수 없습니다.", @@ -722,6 +723,7 @@ "unable_to_submit_job": "작업을 수행할 수 없습니다.", "unable_to_trash_asset": "휴지통으로 이동할 수 없습니다.", "unable_to_unlink_account": "계정 연결을 해제할 수 없습니다.", + "unable_to_unlink_motion_video": "모션 비디오 연결을 해제할 수 없습니다.", "unable_to_update_album_cover": "앨범 커버를 변경할 수 없습니다.", "unable_to_update_album_info": "앨범 정보를 변경할 수 없습니다.", "unable_to_update_library": "라이브러리를 업데이트할 수 없습니다.", @@ -1259,6 +1261,7 @@ "sunrise_on_the_beach": "동해안에서 맞이하는 새해 일출", "support": "지원", "support_and_feedback": "지원 & 제안", + "support_third_party_description": "Immich가 서드파티 패키지로 설치 되었습니다. 링크를 눌러 먼저 패키지 문제인지 확인해 보세요.", "swap_merge_direction": "병합 방향 변경", "sync": "동기화", "tag": "태그", diff --git a/web/src/lib/i18n/lb.json b/i18n/lb.json similarity index 100% rename from web/src/lib/i18n/lb.json rename to i18n/lb.json diff --git a/web/src/lib/i18n/lt.json b/i18n/lt.json similarity index 81% rename from web/src/lib/i18n/lt.json rename to i18n/lt.json index 4d4db478c9..399ef31c3e 100644 --- a/web/src/lib/i18n/lt.json +++ b/i18n/lt.json @@ -16,7 +16,7 @@ "add_exclusion_pattern": "Pridėti išimčių šabloną", "add_import_path": "Pridėti importavimo kelią", "add_location": "Pridėti vietovę", - "add_more_users": "Pridėti daugiau vartotojų", + "add_more_users": "Pridėti daugiau naudotojų", "add_partner": "Pridėti partnerį", "add_path": "Pridėti kelią", "add_photos": "Pridėti nuotraukų", @@ -25,7 +25,7 @@ "add_to_shared_album": "Pridėti į bendrinamą albumą", "added_to_archive": "Pridėta į archyvą", "added_to_favorites": "Pridėta prie mėgstamiausių", - "added_to_favorites_count": "{count, number} pridėta prie mėgstamiausių", + "added_to_favorites_count": "{count, plural, one {# pridėtas} few {# pridėti} other {# pridėta}} prie mėgstamiausių", "admin": { "authentication_settings": "Autentifikavimo nustatymai", "authentication_settings_description": "Tvarkyti slaptažodžių, OAuth ir kitus autentifikavimo parametrus", @@ -44,7 +44,9 @@ "exclusion_pattern_description": "Išimčių šablonai leidžia nepaisyti failų ir aplankų skenuojant jūsų biblioteką. Tai yra naudinga, jei turite aplankų su failais, kurių nenorite importuoti, pavyzdžiui, RAW failai.", "external_library_created_at": "Išorinė biblioteka (sukurta {date})", "external_library_management": "Išorinių bibliotekų tvarkymas", - "face_detection": "Veido atpažinimas", + "face_detection": "Veidų aptikimas", + "face_detection_description": "Veidų aptikimas bibliotekos elementuose naudojant mašininį mokymąsi. Vaizdo įrašų atveju naudojama tik miniatiūra. \"Atnaujinti\" iš naujo nuskaito visus bibliotekos elementus. \"Atstatyti\" ne tik atnaujina, bet ir išvalo visus esamus veidų duomenis. \"Trūkstami\" nuskaito tik dar nenuskaitytus bibliotekos elementus. Veidų aptikimo darbui pasibaigus, aptikti veidai patenka į veidų atpažinimo darbų eilę, kur jie priskiriami jau esamiems ar naujai atpažintiems žmonėms.", + "facial_recognition_job_description": "Aptiktų veidų atpažinimas ir priskyrimas žmonėms. Šis darbas vykdomas pasibaigus \"veidų aptikimo\" darbui. \"Atstatyti\" (per)grupuoja visus aptiktus veidus. \"Trūkstami\" apdoroja jokiam žmogui dar nepriskirtus aptiktus veidus.", "failed_job_command": "Darbo {job} komanda {command} nepavyko", "force_delete_user_warning": "ĮSPĖJIMAS: Šis veiksmas iš karto pašalins naudotoją ir visą jo informaciją. Šis žingsnis nesugrąžinamas ir failų nebus galima atkurti.", "forcing_refresh_library_files": "Priverstinai atnaujinami visi failai bilbiotekoje", @@ -65,7 +67,7 @@ "image_thumbnail_resolution_description": "Naudojama žiūrint nuotraukų grupes (pagrindinis nuotraukų puslapis, albumų peržiūra ir t.t.). Aukštesnė rezoliucija gali išlaikyti daugiau detalių, bet užtrunka ilgiau apdoroti, gali turėti didesnius failų dydžius ir gali sumažinti programos greitumą.", "job_concurrency": "{job} lygiagretumas", "job_not_concurrency_safe": "Šis darbas nėra saugus apdoroti lygiagrečiai.", - "job_settings": "Darbo nustatymai", + "job_settings": "Darbų nustatymai", "job_settings_description": "Keisti darbų lygiagretumą", "job_status": "Darbų būsenos", "library_created": "Sukurta biblioteka: {library}", @@ -93,19 +95,19 @@ "machine_learning_duplicate_detection_setting_description": "", "machine_learning_enabled": "Įgalinti mašininį mokymąsi", "machine_learning_enabled_description": "Jei išjungta, visos „ML“ funkcijos bus išjungtos, nepaisant toliau pateiktų nustatymų.", - "machine_learning_facial_recognition": "Veido atpažinimas", + "machine_learning_facial_recognition": "Veidų atpažinimas", "machine_learning_facial_recognition_description": "Aptikti, atpažinti ir sugrupuoti veidus nuotraukose", - "machine_learning_facial_recognition_model": "Veido atpažinimo modelis", + "machine_learning_facial_recognition_model": "Veidų atpažinimo modelis", "machine_learning_facial_recognition_model_description": "", - "machine_learning_facial_recognition_setting": "Įgalinti veido atpažinimą", + "machine_learning_facial_recognition_setting": "Įgalinti veidų atpažinimą", "machine_learning_facial_recognition_setting_description": "", - "machine_learning_max_detection_distance": "", + "machine_learning_max_detection_distance": "Maksimalus aptikimo atstumas", "machine_learning_max_detection_distance_description": "Didžiausias atstumas tarp dviejų vaizdų, kad jie būtų laikomi dublikatais, svyruoja nuo 0,001 iki 0,1. Didesnės vertės aptiks daugiau dublikatų, tačiau gali būti klaidingai teigiami.", "machine_learning_max_recognition_distance": "Maksimalus atpažinimo atstumas", "machine_learning_max_recognition_distance_description": "", "machine_learning_min_detection_score": "", "machine_learning_min_detection_score_description": "", - "machine_learning_min_recognized_faces": "", + "machine_learning_min_recognized_faces": "Mažiausias atpažintų veidų skaičius", "machine_learning_min_recognized_faces_description": "Mažiausias atpažintų veidų skaičius asmeniui, kurį reikia sukurti. Tai padidinus, veido atpažinimas tampa tikslesnis, bet padidėja tikimybė, kad veidas žmogui nepriskirtas.", "machine_learning_settings": "Mašininio mokymosi nustatymai", "machine_learning_settings_description": "Tvarkyti mašininio mokymosi funkcijas ir nustatymus", @@ -114,17 +116,22 @@ "machine_learning_smart_search_enabled": "Įjungti išmaniąją paiešką", "machine_learning_smart_search_enabled_description": "Jei išjungta, vaizdai nebus užkoduoti išmaniajai paieškai.", "machine_learning_url_description": "Mašininio mokymosi serverio URL", + "manage_concurrency": "Tvarkyti lygiagretumą", "manage_log_settings": "", "map_dark_style": "Tamsioji tema", "map_enable_description": "", + "map_gps_settings": "Žemėlapio ir GPS nustatymai", + "map_gps_settings_description": "Tvarkyti žemėlapio ir GPS (atvirkštinio geokodavimo) nustatymus", "map_light_style": "Šviesioji tema", + "map_manage_reverse_geocoding_settings": "Tvarkyti atvirkštinio geokodavimo nustatymus", "map_reverse_geocoding": "Atvirkštinis geokodavimas", - "map_reverse_geocoding_enable_description": "", + "map_reverse_geocoding_enable_description": "Įjungti atvirkštinį geokodavimą", "map_reverse_geocoding_settings": "Atvirkštinio geokodavimo nustatymai", - "map_settings": "Žemėlapio nustatymai", + "map_settings": "Žemėlapis", "map_settings_description": "Tvarkyti žemėlapio parametrus", "map_style_description": "", - "metadata_extraction_job_description": "", + "metadata_extraction_job": "Metaduomenų nuskaitymas", + "metadata_extraction_job_description": "Kiekvieno bibliotekos elemento metaduomenų nuskaitymas, tokių kaip GPS koordinatės, veidai ar rezoliucija", "migration_job_description": "", "no_paths_added": "Keliai nepridėti", "no_pattern_added": "Šablonas nepridėtas", @@ -172,9 +179,9 @@ "password_settings_description": "Tvarkyti prisijungimo slaptažodžiu nustatymus", "paths_validated_successfully": "Visi keliai patvirtinti sėkmingai", "refreshing_all_libraries": "Perkraunamos visos bibliotekos", - "registration_description": "Kadangi esate pirmasis šio sistemos vartotojas, jums bus priskirta administratorius rolė, ir būsite atsakingas už administracines užduotis ir papildomų vartotojų kūrimą.", + "registration_description": "Kadangi esate pirmasis šio sistemos naudotojas, jums bus priskirta administratoriaus rolė, ir būsite atsakingas už administracines užduotis ir papildomų naudotojų kūrimą.", "repair_all": "Pataisyti visus", - "require_password_change_on_login": "Reikalauti, kad vartotojas pasikeistų slaptažodį po pirmojo prisijungimo", + "require_password_change_on_login": "Reikalauti, kad naudotojas pasikeistų slaptažodį po pirmojo prisijungimo", "reset_settings_to_default": "Atstatyti nustatymus į numatytuosius", "server_external_domain_settings": "Išorinis domenas", "server_external_domain_settings_description": "", @@ -184,7 +191,7 @@ "server_welcome_message_description": "Žinutė, rodoma prisijungimo puslapyje.", "sidecar_job_description": "", "slideshow_duration_description": "", - "smart_search_job_description": "", + "smart_search_job_description": "Vykdykite mašininį mokymąsi bibliotekos elementų išmaniajai paieškai", "storage_template_enable_description": "", "storage_template_hash_verification_enabled": "", "storage_template_hash_verification_enabled_description": "", @@ -192,12 +199,13 @@ "storage_template_settings": "", "storage_template_settings_description": "", "system_settings": "Sistemos nustatymai", + "tag_cleanup_job": "Žymų išvalymas", "theme_custom_css_settings": "", "theme_custom_css_settings_description": "", "theme_settings": "Temos nustatymai", "theme_settings_description": "", "thumbnail_generation_job": "Generuoti miniatiūras", - "thumbnail_generation_job_description": "", + "thumbnail_generation_job_description": "Didelių, mažų ir neryškių miniatiūrų generavimas kiekvienam bibliotekos elementui, taip pat miniatiūrų generavimas kiekvienam asmeniui", "transcode_policy_description": "", "transcoding_acceleration_api": "Spartinimo API", "transcoding_acceleration_api_description": "", @@ -210,7 +218,7 @@ "transcoding_accepted_containers": "Priimami konteineriai", "transcoding_accepted_video_codecs": "", "transcoding_accepted_video_codecs_description": "", - "transcoding_advanced_options_description": "Parinktys, kurių daugelis vartotojų keisti neturėtų", + "transcoding_advanced_options_description": "Parinktys, kurių daugelis naudotojų keisti neturėtų", "transcoding_audio_codec": "Garso kodekas", "transcoding_audio_codec_description": "Opus yra aukščiausios kokybės variantas, tačiau turi mažesnį suderinamumą su senesniais įrenginiais ar programine įranga.", "transcoding_bitrate_description": "Vaizdo įrašai viršija maksimalią leistiną bitų spartą arba nėra priimtino formato", @@ -263,15 +271,17 @@ "untracked_files": "Nesekami failai", "user_delete_delay_settings": "Ištrynimo delsa", "user_delete_delay_settings_description": "", - "user_password_has_been_reset": "Vartotojo slaptažodis buvo iš naujo nustatytas:", - "user_restore_description": "Vartotojo {user} paskyra bus atkurta.", - "user_settings": "Vartotojo nustatymai", - "user_settings_description": "Valdyti vartotojo nustatymus", - "user_successfully_removed": "Vartotojas {email} sėkmingai pašalintas.", + "user_management": "Naudotojų valdymas", + "user_password_has_been_reset": "Naudotojo slaptažodis buvo iš naujo nustatytas:", + "user_restore_description": "Naudotojo {user} paskyra bus atkurta.", + "user_settings": "Naudotojo nustatymai", + "user_settings_description": "Valdyti naudotojo nustatymus", + "user_successfully_removed": "Naudotojas {email} sėkmingai pašalintas.", "version_check_enabled_description": "", "version_check_settings": "Versijos tikrinimas", "version_check_settings_description": "Įjungti/išjungti naujos versijos pranešimus", - "video_conversion_job_description": "" + "video_conversion_job": "Vaizdo įrašų konvertavimas", + "video_conversion_job_description": "Vaizdo įrašų konvertavimas platesniam suderinamumui su naršyklėmis ir įrenginiais" }, "admin_email": "Administratoriaus el. paštas", "admin_password": "Administratoriaus slaptažodis", @@ -280,20 +290,21 @@ "album_added": "Albumas pridėtas", "album_added_notification_setting_description": "Gauti el. pašto pranešimą, kai būsite pridėtas prie bendrinamo albumo", "album_cover_updated": "Albumo viršelis atnaujintas", - "album_delete_confirmation": "Ar tikrai norite ištrinti albumą {album}?\nJei šis albumas yra bendrinamas, kiti vartotojai nebegalės jo pasiekti.", + "album_delete_confirmation": "Ar tikrai norite ištrinti albumą {album}?", "album_info_updated": "Albumo informacija atnaujinta", "album_leave": "Palikti albumą?", "album_leave_confirmation": "Ar tikrai norite palikti albumą {album}?", "album_name": "Albumo pavadinimas", "album_options": "Albumo parinktys", - "album_remove_user": "Pašalinti vartotoją?", - "album_remove_user_confirmation": "Ar tikrai norite pašalinti vartotoją {user}?", - "album_share_no_users": "Atrodo, kad bendrinate šį albumą su visais vartotojais, arba neturite vartotojų, su kuriais galėtumėte bendrinti.", + "album_remove_user": "Pašalinti naudotoją?", + "album_remove_user_confirmation": "Ar tikrai norite pašalinti naudotoją {user}?", + "album_share_no_users": "Atrodo, kad bendrinate šį albumą su visais naudotojais, arba neturite naudotojų, su kuriais galėtumėte bendrinti.", "album_updated": "Albumas atnaujintas", "album_updated_setting_description": "Gauti pranešimą el. paštu, kai bendrinamas albumas turi naujų elementų", "album_user_removed": "Pašalintas {user}", "album_with_link_access": "Tegul visi, turintys nuorodą, mato šio albumo nuotraukas ir žmones.", "albums": "Albumai", + "albums_count": "{count, plural, one {# albumas} few {# albumai} other {# albumų}}", "all": "Visi", "all_albums": "Visi albumai", "all_people": "Visi žmonės", @@ -307,11 +318,12 @@ "api_keys": "API raktai", "app_settings": "Programos nustatymai", "appears_in": "", - "archive": "", + "archive": "Archyvas", "archive_or_unarchive_photo": "Archyvuoti arba išarchyvuoti nuotrauką", "archive_size": "Archyvo dydis", "archive_size_description": "Konfigūruoti archyvo dydį atsisiuntimams (GiB)", "archived": "", + "archived_count": "{count, plural, other {# suarchyvuota}}", "are_these_the_same_person": "Ar tai tas pats asmuo?", "are_you_sure_to_do_this": "Ar tikrai norite tai daryti?", "asset_added_to_album": "Pridėta į albumą", @@ -320,13 +332,23 @@ "asset_offline": "", "asset_uploaded": "Įkelta", "asset_uploading": "Įkeliama...", - "assets": "", + "assets": "Elementai", + "assets_added_count": "{count, plural, one {Pridėtas # elementas} few {Pridėti # elementai} other {Pridėta # elementų}}", + "assets_added_to_album_count": "Į albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementų}}", + "assets_added_to_name_count": "Į {hasName, select, true {{name}} other {naują}} albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementų}}", + "assets_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}}", + "assets_moved_to_trash_count": "{count, plural, one {# elementas perkeltas} few {# elementai perkelti} other {# elementų perkelta}} į šiukšliadėžę", + "assets_permanently_deleted_count": "{count, plural, one {# elementas ištrintas} few {# elementai ištrinti} other {# elementų ištrinta}} visam laikui", + "assets_removed_count": "{count, plural, one {Pašalintas # elementas} few {Pašalinti # elementai} other {Pašalinta # elementų}}", + "assets_restored_count": "{count, plural, one {Atkurtas # elementas} few {Atkurti # elementai} other {Atkurta # elementų}}", + "assets_were_part_of_album_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}} jau prieš tai buvo albume", "authorized_devices": "Autorizuoti įrenginiai", "back": "Atgal", "back_close_deselect": "Atgal, uždaryti arba atžymėti", "backward": "", "birthdate_saved": "Sėkmingai išsaugota gimimo data", "blurred_background": "Neryškus fonas", + "bugs_and_feature_requests": "Klaidų ir funkcijų užklausos", "buy": "Įsigyti Immich", "camera": "Fotoaparatas", "camera_brand": "Fotoaparato prekės ženklas", @@ -391,7 +413,9 @@ "create_new_person": "Sukurti naują žmogų", "create_new_person_hint": "Priskirti pasirinktus elementus naujam žmogui", "create_new_user": "Sukurti naują varotoją", - "create_user": "Sukurti vartotoją", + "create_tag": "Sukurti žymą", + "create_tag_description": "Sukurti naują žymą. Įdėtinėms žymoms įveskite pilną kelią, įskaitant pasviruosius brūkšnius.", + "create_user": "Sukurti naudotoją", "created": "Sukurta", "current_device": "Dabartinis įrenginys", "custom_locale": "", @@ -413,7 +437,9 @@ "delete_library": "Ištrinti biblioteką", "delete_link": "Ištrinti nuorodą", "delete_shared_link": "Ištrinti bendrinamą nuorodą", - "delete_user": "Ištrinti vartotoją", + "delete_tag": "Ištrinti žymą", + "delete_tag_confirmation_prompt": "Ar tikrai norite ištrinti žymą {tagName}?", + "delete_user": "Ištrinti naudotoją", "deleted_shared_link": "Bendrinama nuoroda ištrinta", "description": "Aprašymas", "details": "Detalės", @@ -428,6 +454,7 @@ "display_original_photos": "Rodyti originalias nuotraukas", "display_original_photos_setting_description": "", "do_not_show_again": "Daugiau nerodyti šio pranešimo", + "documentation": "Dokumentacija", "done": "", "download": "Atsisiųsti", "download_settings": "Atsisiųsti", @@ -455,8 +482,9 @@ "edit_location": "Redaguoti vietovę", "edit_name": "Redaguoti vardą", "edit_people": "Redaguoti žmones", + "edit_tag": "Redaguoti žymą", "edit_title": "Redaguoti antraštę", - "edit_user": "Redaguoti vartotoją", + "edit_user": "Redaguoti naudotoją", "edited": "Redaguota", "editor": "", "email": "El. paštas", @@ -472,7 +500,7 @@ "errors": { "cant_apply_changes": "Negalima taikyti pakeitimų", "error_adding_assets_to_album": "Klaida pridedant elementus į albumą", - "error_adding_users_to_album": "Klaida pridedant vartotojus prie albumo", + "error_adding_users_to_album": "Klaida pridedant naudotojus prie albumo", "error_downloading": "Klaida atsisiunčiant {filename}", "error_hiding_buy_button": "Klaida slepiant pirkimo mygtuką", "error_removing_assets_from_album": "Klaida šalinant elementus iš albumo, patikrinkite konsolę dėl išsamesnės informacijos", @@ -482,16 +510,18 @@ "failed_to_edit_shared_link": "Nepavyko redaguoti bendrinamos nuorodos", "failed_to_load_people": "Nepavyko užkrauti žmonių", "failed_to_remove_product_key": "Nepavyko pašalinti produkto rakto", + "failed_to_stack_assets": "Nepavyko sugrupuoti elementų", + "failed_to_unstack_assets": "Nepavyko išgrupuoti elementų", "import_path_already_exists": "Šis importavimo kelias jau egzistuoja.", "incorrect_email_or_password": "Neteisingas el. pašto adresas arba slaptažodis", "profile_picture_transparent_pixels": "Profilio nuotrauka negali turėti permatomų pikselių. Prašome priartinti ir/arba perkelkite nuotrauką.", "quota_higher_than_disk_size": "Nustatyta kvota, viršija disko dydį", - "unable_to_add_album_users": "Nepavyksta pridėti vartotojų prie albumo", + "unable_to_add_album_users": "Nepavyksta pridėti naudotojų prie albumo", "unable_to_add_comment": "Nepavyksta pridėti komentaro", "unable_to_add_exclusion_pattern": "Nepavyksta pridėti išimčių šablono", "unable_to_add_import_path": "Nepavyksta pridėti importavimo kelio", "unable_to_add_partners": "Nepavyksta pridėti partnerių", - "unable_to_change_album_user_role": "Nepavyksta pakeisti albumo vartoto rolės", + "unable_to_change_album_user_role": "Nepavyksta pakeisti albumo naudotojo rolės", "unable_to_change_date": "Negalima pakeisti datos", "unable_to_change_location": "Negalima pakeisti vietos", "unable_to_change_password": "Negalima pakeisti slaptažodžio", @@ -503,13 +533,13 @@ "unable_to_create_admin_account": "Nepavyko sukurti administratoriaus paskyros", "unable_to_create_api_key": "Nepavyko sukurti naujo API rakto", "unable_to_create_library": "Nepavyko sukurti bibliotekos", - "unable_to_create_user": "Nepavyko sukurti vartotojo", + "unable_to_create_user": "Nepavyko sukurti naudotojo", "unable_to_delete_album": "Nepavyksta ištrinti albumo", "unable_to_delete_asset": "", "unable_to_delete_exclusion_pattern": "Nepavyksta ištrinti išimčių šablono", "unable_to_delete_import_path": "Nepavyksta ištrinti importavimo kelio", "unable_to_delete_shared_link": "Nepavyksta ištrinti bendrinimo nuorodos", - "unable_to_delete_user": "Nepavyksta ištrinti vartotojo", + "unable_to_delete_user": "Nepavyksta ištrinti naudotojo", "unable_to_edit_exclusion_pattern": "Nepavyksta redaguoti išimčių šablono", "unable_to_edit_import_path": "Nepavyksta redaguoti išimčių kelio", "unable_to_empty_trash": "", @@ -525,7 +555,7 @@ "unable_to_log_out_device": "Nepavyksta atjungti įrenginio", "unable_to_login_with_oauth": "Nepavyksta prisijungti su OAuth", "unable_to_play_video": "Nepavyksta paleisti vaizdo įrašo", - "unable_to_refresh_user": "Nepavyksta atnaujinti vartotojo", + "unable_to_refresh_user": "Nepavyksta atnaujinti naudotojo", "unable_to_remove_album_users": "", "unable_to_remove_api_key": "Nepavyko pašalinti API rakto", "unable_to_remove_comment": "", @@ -574,8 +604,8 @@ "external_libraries": "Išorinės bibliotekos", "face_unassigned": "Nepriskirta", "failed_to_get_people": "", - "favorite": "Mėgstamiausias", - "favorite_or_unfavorite_photo": "", + "favorite": "Mėgstamiausi", + "favorite_or_unfavorite_photo": "Įtraukti prie arba pašalinti iš mėgstamiausių", "favorites": "Mėgstamiausi", "feature": "", "feature_photo_updated": "", @@ -587,6 +617,7 @@ "filetype": "Failo tipas", "filter_people": "Filtruoti žmones", "fix_incorrect_match": "", + "folders": "Aplankai", "force_re-scan_library_files": "", "forward": "", "general": "", @@ -628,6 +659,7 @@ }, "invite_people": "Kviesti žmones", "invite_to_album": "Pakviesti į albumą", + "items_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}}", "job_settings_description": "", "jobs": "Darbai", "keep": "Palikti", @@ -683,6 +715,7 @@ "merge_people_limit": "Vienu metu galite sujungti tik iki 5 veidų", "merge_people_prompt": "Ar norite sujungti šiuos asmenis? Šis veiksmas yra negrįžtamas.", "merge_people_successfully": "Asmenys sėkmingai sujungti", + "merged_people_count": "{count, plural, one {Sujungtas # asmuo} few {Sujungti # asmenys} other {Sujungta # asmenų}}", "minimize": "Sumažinti", "minute": "Minutė", "missing": "Trūkstami", @@ -694,11 +727,11 @@ "name": "Vardas", "name_or_nickname": "Vardas arba slapyvardis", "never": "Niekada", - "new_album": "", + "new_album": "Naujas albumas", "new_api_key": "Naujas API raktas", "new_password": "Naujas slaptažodis", "new_person": "Naujas asmuo", - "new_user_created": "Sukurtas naujas vartotojas", + "new_user_created": "Naujas naudotojas sukurtas", "new_version_available": "PRIEINAMA NAUJA VERSIJA", "newest_first": "Pirmiausia naujausi", "next": "Sekantis", @@ -726,6 +759,7 @@ "notifications": "Pranešimai", "notifications_setting_description": "Tvarkyti pranešimus", "oauth": "", + "official_immich_resources": "Oficialūs Immich ištekliai", "offline": "Neprisijungęs", "ok": "Ok", "oldest_first": "Seniausias pirmas", @@ -765,12 +799,15 @@ "paused": "Sustabdyta", "pending": "Laukiama", "people": "Asmenys", + "people_edits_count": "{count, plural, one {Redaguotas # asmuo} few {Redaguoti # asmenys} other {Redaguota # asmenų}}", "people_sidebar_description": "", "perform_library_tasks": "", "permanent_deletion_warning": "", "permanent_deletion_warning_setting_description": "", "permanently_delete": "Ištrinti visam laikui", + "permanently_delete_assets_count": "Visam laikui ištrinti {count, plural, one {# elementą} few {# elementus} other {# elementų}}", "permanently_deleted_asset": "", + "permanently_deleted_assets_count": "Visam laikui {count, plural, one {ištrintas # elementas} few {ištrinti # elementai} other {ištrinta # elementų}}", "photos": "Nuotraukos", "photos_and_videos": "Nuotraukos ir vaizdo įrašai", "photos_count": "{count, plural, one {{count, number} nuotrauka} few {{count, number} nuotraukos} other {{count, number} nuotraukų}}", @@ -811,8 +848,8 @@ "purchase_license_subtitle": "Įsigykite „Immich“, kad palaikytumėte tolesnį paslaugos vystymą", "purchase_lifetime_description": "Pirkimas visam gyvenimui", "purchase_option_title": "PIRKIMO PASIRINKIMAS", - "purchase_panel_info_1": "„Immich“ kūrimas užima daug laiko ir pastangų, o visą darbo dieną dirba inžinieriai, kad jis būtų kuo geresnis. Mūsų misija yra, kad atvirojo kodo programinė įranga ir etiška verslo praktika taptų tvariu programuotojų pajamų šaltiniu ir sukurtų privatumą gerbiančią ekosistemą su realiomis alternatyvomis išnaudojamoms debesijos paslaugoms.", - "purchase_panel_info_2": "Kadangi esame įsipareigoję nepridėti mokamų sienų, šis pirkinys nesuteiks jums jokių papildomų „Immich“ funkcijų. Mes tikime, kad tokie vartotojai kaip jūs palaikys nuolatinį „Immich“ vystymąsi.", + "purchase_panel_info_1": "„Immich“ kūrimas užima daug laiko ir pastangų, o visą darbo dieną dirba inžinieriai, kad jis būtų kuo geresnis. Mūsų misija yra, kad atvirojo kodo programinė įranga ir etiška verslo praktika taptų tvariu kūrėjų pajamų šaltiniu ir sukurtų privatumą gerbiančią ekosistemą su realiomis alternatyvomis išnaudojamoms debesijos paslaugoms.", + "purchase_panel_info_2": "Kadangi esame įsipareigoję nepridėti mokamų sienų, šis pirkinys nesuteiks jums jokių papildomų „Immich“ funkcijų. Mes tikime, kad tokie naudotojai kaip jūs palaikys nuolatinį „Immich“ vystymąsi.", "purchase_panel_title": "Palaikykite projektą", "purchase_per_server": "Vienam serveriui", "purchase_per_user": "Vienam naudotojui", @@ -826,34 +863,39 @@ "purchase_settings_server_activated": "Serverio produkto raktas yra tvarkomas administratoriaus", "range": "", "rating": "Įvertinimas žvaigždutėmis", + "rating_count": "{count, plural, one {# įvertinimas} few {# įvertinimai} other {# įvertinimų}}", "raw": "", "reaction_options": "", "read_changelog": "", "recent": "", "recent_searches": "", - "refresh": "", - "refreshed": "", + "refresh": "Atnaujinti", + "refreshed": "Atnaujinta", "refreshes_every_file": "", "remove": "Pašalinti", "remove_deleted_assets": "", "remove_from_album": "Pašalinti iš albumo", "remove_from_favorites": "Pašalinti iš mėgstamiausių", "remove_from_shared_link": "", - "remove_user": "Pašalinti vartotoją", + "remove_user": "Pašalinti naudotoją", "removed_api_key": "Pašalintas API Raktas: {name}", + "removed_from_archive": "Pašalinta iš archyvo", + "removed_from_favorites": "Pašalinta iš mėgstamiausių", + "removed_from_favorites_count": "{count, plural, one {# pašalintas} few {# pašalinti} other {# pašalinta}} iš mėgstamiausių", + "removed_tagged_assets": "Žyma pašalinta iš {count, plural, one {# elemento} other {# elementų}}", "rename": "Pervadinti", "repair": "Pataisyti", "repair_no_results_message": "", "replace_with_upload": "", "require_password": "Reikalauti slaptažodžio", - "reset": "", + "reset": "Atstatyti", "reset_password": "", "reset_people_visibility": "", "reset_settings_to_default": "", "resolved_all_duplicates": "Išspręsti visi dublikatai", "restore": "Atkurti", "restore_all": "Atkurti visus", - "restore_user": "Atkurti vartotoją", + "restore_user": "Atkurti naudotoją", "retry_upload": "", "review_duplicates": "", "role": "", @@ -864,8 +906,9 @@ "say_something": "Ką nors pasakykite", "scan_all_libraries": "Skenuoti visas bibliotekas", "scan_all_library_files": "", + "scan_library": "Skenuoti", "scan_new_library_files": "", - "scan_settings": "", + "scan_settings": "Skenavimo nustatymai", "search": "Ieškoti", "search_albums": "", "search_by_context": "Ieškoti pagal kontekstą", @@ -874,12 +917,14 @@ "search_camera_make": "", "search_camera_model": "", "search_city": "", - "search_country": "", + "search_country": "Ieškoti šalies...", "search_for_existing_person": "", "search_no_people_named": "Nėra žmonių vardu „{name}“", - "search_people": "", - "search_places": "", + "search_people": "Ieškoti žmonių", + "search_places": "Ieškoti vietų", + "search_settings": "Ieškoti nustatymų", "search_state": "", + "search_tags": "Ieškoti žymų...", "search_timezone": "", "search_type": "Paieškos tipas", "search_your_photos": "Ieškoti nuotraukų", @@ -890,14 +935,17 @@ "select_all_duplicates": "Pasirinkti visus dublikatus", "select_avatar_color": "Pasirinkti avataro spalvą", "select_face": "Pasirinkti veidą", - "select_featured_photo": "", - "select_library_owner": "", + "select_featured_photo": "Pasirinkti rodomą nuotrauką", + "select_library_owner": "Pasirinkti bibliotekos savininką", "select_new_face": "", "select_photos": "", "selected": "Pasirinkta", + "selected_count": "{count, plural, one {# pasirinktas} few {# pasirinkti} other {# pasirinktų}}", "send_message": "Siųsti žinutę", "send_welcome_email": "Siųsti sveikinimo el. laišką", "server": "Serveris", + "server_offline": "Serveris nepasiekiamas", + "server_online": "Serveris pasiekiamas", "server_stats": "Serverio statistika", "server_version": "Serverio versija", "set": "Nustatyti", @@ -913,6 +961,7 @@ "shared_by": "", "shared_by_you": "", "shared_links": "", + "shared_photos_and_videos_count": "{assetCount, plural, one {# bendrinama nuotrauka ir vaizdo įrašas} few {# bendrinamos nuotraukos ir vaizdo įrašai} other {# bendrinamų nuotraukų ir vaizdo įrašų}}", "shared_with_partner": "Pasidalinta su {partner}", "sharing": "Dalijimasis", "sharing_enter_password": "Norėdami peržiūrėti šį puslapį, įveskite slaptažodį.", @@ -930,6 +979,8 @@ "show_person_options": "", "show_progress_bar": "", "show_search_options": "Rodyti paieškos parinktis", + "show_supporter_badge": "Rėmėjo ženklelis", + "show_supporter_badge_description": "Rodyti rėmėjo ženklelį", "shuffle": "", "sign_out": "Atsijungti", "sign_up": "Užsiregistruoti", @@ -944,9 +995,13 @@ "sort_recent": "Naujausia nuotrauka", "sort_title": "Pavadinimas", "source": "Šaltinis", - "stack": "", - "stack_selected_photos": "", + "stack": "Grupuoti", + "stack_duplicates": "Grupuoti dublikatus", + "stack_select_one_photo": "Pasirinkti pagrindinę grupės nuotrauką", + "stack_selected_photos": "Grupuoti pasirinktas nuotraukas", + "stacked_assets_count": "{count, plural, one {Sugrupuotas # elementas} few {Sugrupuoti # elementai} other {Sugrupuota # elementų}}", "stacktrace": "", + "start": "Pradėti", "start_date": "Pradžios data", "state": "", "status": "Statusas", @@ -957,17 +1012,24 @@ "submit": "Pateikti", "suggestions": "", "sunrise_on_the_beach": "Saulėtekis paplūdimyje", + "support_and_feedback": "Palaikymas ir atsiliepimai", "swap_merge_direction": "", "sync": "Sinchronizuoti", + "tag": "Žyma", + "tag_created": "Sukurta žyma: {tag}", + "tag_not_found_question": "Nerandate žymos? Sukurti naują žymą.", + "tag_updated": "Atnaujinta žyma: {tag}", + "tagged_assets": "Žyma pridėta prie {count, plural, one {# elemento} other {# elementų}}", + "tags": "Žymos", "template": "Šablonas", "theme": "Tema", "theme_selection": "", "theme_selection_description": "", "time_based_memories": "", "timezone": "Laiko juosta", - "to_archive": "Archyvas", + "to_archive": "Archyvuoti", "to_change_password": "Pakeisti slaptažodį", - "to_favorite": "Mėgstamiausi", + "to_favorite": "Įtraukti prie mėgstamiausių", "toggle_settings": "", "toggle_theme": "", "toggle_visibility": "", @@ -976,10 +1038,12 @@ "trash_all": "Ištrinti visus", "trash_count": "Šiukšliadėžė {count, number}", "trash_no_results_message": "", - "type": "", + "trashed_items_will_be_permanently_deleted_after": "Į šiukšliadėžę perkelti elementai bus visam laikui ištrinti po {days, plural, one {# dienos} other {# dienų}}.", + "type": "Tipas", "unarchive": "Išarchyvuoti", "unarchived": "", - "unfavorite": "", + "unarchived_count": "{count, plural, other {# išarchyvuota}}", + "unfavorite": "Pašalinti iš mėgstamiausių", "unhide_person": "", "unknown": "", "unknown_album": "", @@ -988,7 +1052,8 @@ "unlinked_oauth_account": "", "unsaved_change": "Neišsaugoti pakeitimai", "unselect_all": "", - "unstack": "", + "unstack": "Išgrupuoti", + "unstacked_assets_count": "{count, plural, one {Išgrupuotas # elementas} few {Išgrupuoti # elementai} other {Išgrupuota # elementų}}", "up_next": "", "updated_password": "Slaptažodis atnaujintas", "upload": "Įkelti", @@ -999,26 +1064,30 @@ "upload_status_uploaded": "Įkelta", "url": "", "usage": "", - "user": "Vartotojas", - "user_id": "Vartotojo ID", + "user": "Naudotojas", + "user_id": "Naudotojo ID", "user_usage_detail": "", - "username": "Vartotojo vardas", - "users": "Vartotojai", - "utilities": "", + "username": "Naudotojo vardas", + "users": "Naudotojai", + "utilities": "Priemonės", "validate": "", "variables": "Kintamieji", "version": "Versija", "version_announcement_closing": "Tavo draugas, Alex", + "version_history": "Versijų istorija", + "version_history_item": "Versija {version} įdiegta {date}", "video": "Vaizdo įrašas", "video_hover_setting_description": "", "videos": "Video", + "videos_count": "{count, plural, one {# vaizdo įrašas} few {# vaizdo įrašai} other {# vaizdo įrašų}}", "view": "Rodyti", "view_album": "Rodyti albumą", "view_all": "", - "view_all_users": "Rodyti visus vartotojus", + "view_all_users": "Peržiūrėti visus naudotojus", "view_links": "Rodyti nuorodas", "view_next_asset": "", "view_previous_asset": "", + "view_stack": "Peržiūrėti grupę", "viewer": "", "waiting": "Laukiama", "warning": "Įspėjimas", diff --git a/web/src/lib/i18n/lv.json b/i18n/lv.json similarity index 98% rename from web/src/lib/i18n/lv.json rename to i18n/lv.json index 72605eaad0..614309f857 100644 --- a/web/src/lib/i18n/lv.json +++ b/i18n/lv.json @@ -588,7 +588,7 @@ "log_out_all_devices": "", "login_has_been_disabled": "", "longitude": "Ģeogrāfiskais garums", - "look": "", + "look": "Izskats", "loop_videos": "", "loop_videos_description": "Iespējot, lai automātiski videoklips tiktu cikliski palaists detaļu skatītājā.", "make": "Firma", @@ -650,29 +650,32 @@ "no_shared_albums_message": "", "not_in_any_album": "Nav nevienā albumā", "notes": "Piezīmes", - "notification_toggle_setting_description": "", + "notification_toggle_setting_description": "Ieslēgt e-pasta paziņojumus", "notifications": "Paziņojumi", "notifications_setting_description": "", - "oauth": "", - "offline": "", - "ok": "", + "oauth": "OAuth", + "official_immich_resources": "Oficiālie Immich resursi", + "offline": "Bezsaistē", + "ok": "Labi", "oldest_first": "", - "online": "", + "online": "Tiešsaistē", "only_favorites": "Tikai izlase", "only_refreshes_modified_files": "", + "open_in_map_view": "Atvērt kartes skatā", "open_in_openstreetmap": "Atvērt OpenStreetMap", - "open_the_search_filters": "", + "open_the_search_filters": "Atvērt meklēšanas filtrus", "options": "Iestatījumi", + "or": "vai", "organize_your_library": "", - "other": "", - "other_devices": "", - "other_variables": "", + "other": "Citi", + "other_devices": "Citas ierīces", + "other_variables": "Citi mainīgie", "owned": "Īpašumā", "owner": "Īpašnieks", "partner_sharing": "", "partners": "", "password": "Parole", - "password_does_not_match": "", + "password_does_not_match": "Parole nesakrīt", "password_required": "", "password_reset_success": "", "past_durations": { diff --git a/web/src/lib/i18n/mfa.json b/i18n/mfa.json similarity index 100% rename from web/src/lib/i18n/mfa.json rename to i18n/mfa.json diff --git a/web/src/lib/i18n/mk.json b/i18n/mk.json similarity index 100% rename from web/src/lib/i18n/mk.json rename to i18n/mk.json diff --git a/web/src/lib/i18n/mn.json b/i18n/mn.json similarity index 96% rename from web/src/lib/i18n/mn.json rename to i18n/mn.json index 13658138c2..231c84e4cf 100644 --- a/web/src/lib/i18n/mn.json +++ b/i18n/mn.json @@ -28,7 +28,8 @@ "added_to_favorites_count": "Дуртай зурагнуудад {count, number} нэмэгдлээ", "admin": { "authentication_settings": "Танин нэвтрэлт тохиргоо", - "authentication_settings_description": "", + "authentication_settings_description": "Нууц үгийн удирдлага, OAuth болон бусад танин нэвтрэлтийн тохиргоо", + "authentication_settings_disable_all": "Бүх нэвтрэх аргуудыг идэвхигүй болгохдоо итгэлтэй байна уу? Нэвтрэх үйлдэл бүрэн идэвхигүй болно.", "check_all": "Бүгдийг сонгох", "crontab_guru": "", "disable_login": "", @@ -816,10 +817,14 @@ "view_next_asset": "", "view_previous_asset": "", "viewer": "", - "waiting": "", - "week": "", - "welcome_to_immich": "", - "year": "", - "yes": "", - "zoom_image": "" + "waiting": "Хүлээж байна", + "warning": "Анхааруулга", + "week": "Долоо хоног", + "welcome": "Тавтай морил", + "welcome_to_immich": "Тавтай морилно уу", + "year": "Он", + "years_ago": "{years, plural, one {# year} other {# years}} өмнө", + "yes": "Тийм", + "you_dont_have_any_shared_links": "Танд хуваалцсан холбоос алга", + "zoom_image": "Зургийг томруулж харах" } diff --git a/web/src/lib/i18n/mr.json b/i18n/mr.json similarity index 100% rename from web/src/lib/i18n/mr.json rename to i18n/mr.json diff --git a/web/src/lib/i18n/ms.json b/i18n/ms.json similarity index 100% rename from web/src/lib/i18n/ms.json rename to i18n/ms.json diff --git a/web/src/lib/i18n/nb_NO.json b/i18n/nb_NO.json similarity index 100% rename from web/src/lib/i18n/nb_NO.json rename to i18n/nb_NO.json diff --git a/web/src/lib/i18n/nl.json b/i18n/nl.json similarity index 99% rename from web/src/lib/i18n/nl.json rename to i18n/nl.json index be4feab23e..d437ae17f7 100644 --- a/web/src/lib/i18n/nl.json +++ b/i18n/nl.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Hardware acceleratie", "transcoding_hardware_acceleration_description": "Experimenteel; veel sneller, maar zal een lagere kwaliteit hebben bij dezelfde bitrate", "transcoding_hardware_decoding": "Hardware decodering", - "transcoding_hardware_decoding_setting_description": "Geldt alleen voor NVENC, QSV en RKMPP. Maakt end-to-end versnelling mogelijk in plaats van alleen de codering te versnellen. Werkt mogelijk niet op alle video's.", + "transcoding_hardware_decoding_setting_description": "Maakt end-to-end versnelling mogelijk in plaats van alleen de codering te versnellen. Werkt mogelijk niet op alle video's.", "transcoding_hevc_codec": "HEVC codec", "transcoding_max_b_frames": "Maximum B-Frames", "transcoding_max_b_frames_description": "Hogere waarden verbeteren de compressie efficiëntie, maar vertragen de codering. Is mogelijk niet compatibel met hardwareversnelling op oudere apparaten. 0 schakelt B-frames uit, terwijl -1 deze waarde automatisch instelt.", @@ -888,6 +888,7 @@ "look": "Uiterlijk", "loop_videos": "Video's herhalen", "loop_videos_description": "Inschakelen om video's automatisch te herhalen in de detailweergave.", + "main_branch_warning": "U gebruikt een ontwikkelingsversie. Wij raden u ten zeerste aan een releaseversie te gebruiken!", "make": "Merk", "manage_shared_links": "Beheer gedeelde links", "manage_sharing_with_partners": "Beheer delen met partners", diff --git a/web/src/lib/i18n/pl.json b/i18n/pl.json similarity index 99% rename from web/src/lib/i18n/pl.json rename to i18n/pl.json index 990890487e..26b89008a7 100644 --- a/web/src/lib/i18n/pl.json +++ b/i18n/pl.json @@ -1,7 +1,7 @@ { "about": "O aplikacji", "account": "Konto", - "account_settings": "Ustawienia Konta", + "account_settings": "Ustawienia konta", "acknowledge": "Rozumiem", "action": "Akcja", "actions": "Akcje/i", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Przyspieszenie Sprzętowe", "transcoding_hardware_acceleration_description": "Eksperymentalny; znacznie szybszy, ale będzie miał niższą jakość przy tej samej szybkości transmisji", "transcoding_hardware_decoding": "Dekodowanie sprzętowe", - "transcoding_hardware_decoding_setting_description": "Dotyczy tylko NVENC, QSV i RKMPP. Umożliwia całkowite przyspieszenie sprzętowe zamiast tylko przyspieszania kodowania. Może nie działać we wszystkich filmach.", + "transcoding_hardware_decoding_setting_description": "Umożliwia całkowite przyspieszenie sprzętowe zamiast tylko przyspieszania kodowania. Może nie działać we wszystkich filmach.", "transcoding_hevc_codec": "Kodek HEVC", "transcoding_max_b_frames": "Maksymalne klatki B (B-Frames)", "transcoding_max_b_frames_description": "Wyższe wartości poprawiają wydajność kompresji, ale spowalniają kodowanie. Może nie być kompatybilny z akceleracją sprzętową na starszych urządzeniach. 0 wyłącza klatki B (B-frames), natomiast -1 ustawia tę wartość automatycznie.", @@ -863,6 +863,7 @@ "look": "Wygląd", "loop_videos": "Powtarzaj filmy", "loop_videos_description": "Włącz automatyczne zapętlanie wideo w przeglądarce szczegółów.", + "main_branch_warning": "Używasz wersji deweloperskiej. Rekomendujemy instalację stabilnej wersji aplikacji!", "make": "Marka", "manage_shared_links": "Zarządzaj udostępnionymi linkami", "manage_sharing_with_partners": "Zarządzaj dzieleniem z partnerami", diff --git a/web/src/lib/i18n/pt.json b/i18n/pt.json similarity index 99% rename from web/src/lib/i18n/pt.json rename to i18n/pt.json index 01120e5fcd..dda003243e 100644 --- a/web/src/lib/i18n/pt.json +++ b/i18n/pt.json @@ -279,7 +279,7 @@ "transcoding_audio_codec_description": "Opus é a opção de mais alta qualidade, mas tem menor compatibilidade com dispositivos ou software antigos.", "transcoding_bitrate_description": "Vídeos com taxa de bits superior à máxima ou que não estão num formato aceite", "transcoding_codecs_learn_more": "Para saber mais sobre as terminologias utilizadas aqui, consulte a documentação do FFmpeg para o codec H.264, codec HEVC e codec VP9.", - "transcoding_constant_quality_mode": "Modo de qualidade constante", + "transcoding_constant_quality_mode": "Modo de qualidade fixa", "transcoding_constant_quality_mode_description": "ICQ é melhor que CQP, mas alguns dispositivos de aceleração de hardware não suportam este modo. Definir esta opção dará preferência ao modo especificado ao usar codificação baseada em qualidade. Ignorado pelo NVENC porque não suporta ICQ.", "transcoding_constant_rate_factor": "Fator de taxa constante (-crf)", "transcoding_constant_rate_factor_description": "Nível de qualidade do vídeo. Os valores típicos são 23 para H.264, 28 para HEVC, 31 para VP9 e 35 para AV1. Menor é melhor, mas produz ficheiros maiores.", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Aceleração de hardware", "transcoding_hardware_acceleration_description": "Experimental; muito mais rápido, mas terá qualidade inferior com a mesma taxa de bits", "transcoding_hardware_decoding": "Decodificação de hardware", - "transcoding_hardware_decoding_setting_description": "Aplica-se apenas a NVENC, QSV e RKMPP. Permite aceleração ponta a ponta em vez de apenas acelerar a codificação. Pode não funcionar em todos os vídeos.", + "transcoding_hardware_decoding_setting_description": "Permite a aceleração ponta a ponta em vez de apenas acelerar a codificação. Pode não funcionar em todos os formatos de arquivo.", "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Máximo de quadros B", "transcoding_max_b_frames_description": "Valores mais altos melhoram a eficiência da compressão, mas tornam a codificação mais lenta. Pode não ser compatível com aceleração de hardware em dispositivos mais antigos. 0 desativa os quadros B, enquanto -1 define esse valor automaticamente.", @@ -860,6 +860,7 @@ "look": "Estilo", "loop_videos": "Repetir vídeos", "loop_videos_description": "Ativar para repetir os vídeos automaticamente durante a exibição.", + "main_branch_warning": "Está a utilizar uma versão de desenvolvimento, recomendamos vivamente que utilize uma versão estável!", "make": "Marca", "manage_shared_links": "Gerir links partilhados", "manage_sharing_with_partners": "Gerir partilha com parceiros", diff --git a/web/src/lib/i18n/pt_BR.json b/i18n/pt_BR.json similarity index 98% rename from web/src/lib/i18n/pt_BR.json rename to i18n/pt_BR.json index 7cd05268ed..7b6d5ed40d 100644 --- a/web/src/lib/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -51,8 +51,8 @@ "external_library_created_at": "Biblioteca externa (criada em {date})", "external_library_management": "Gerenciamento de bibliotecas externas", "face_detection": "Detecção de rostos", - "face_detection_description": "Detecta rostos em arquivos com inteligência artificial. Para vídeos, apenas a miniatura é considerada. \"Todos\" (re)processa todos os arquivos. \"Ausente\" enfileira arquivos que ainda não foram processados. Os rostos detectados serão enfileirados para reconhecimento facial após a conclusão da detecção de rostos, agrupando-os em pessoas novas ou existentes.", - "facial_recognition_job_description": "Agrupa rostos detectados em pessoas. Esta etapa é executada após a conclusão da detecção de rostos. \"Todos\" (re)agrupa todos os rostos. \"Ausentes\" enfileira rostos que ainda não têm uma pessoa atribuída.", + "face_detection_description": "Detectar rostos nos arquivos usando aprendizado de máquina. Para vídeos, apenas a miniatura é considerada. ‘Atualizar’ (re)processa todos os arquivos. ‘Resetar’ também limpa todos os dados de rosto atuais. ‘Faltando’ coloca em fila os arquivos que ainda não foram processados. Rostos detectados serão colocados em fila para o Reconhecimento Facial após a conclusão da Detecção de Rostos, agrupando-os em pessoas existentes ou novas.", + "facial_recognition_job_description": "Agrupar rostos detectados em pessoas. Esta etapa é executada após a conclusão da Detecção de Rostos. ‘Resetar’ (re)agrupará todos os rostos. ‘Faltando’ coloca em fila os rostos que não têm uma pessoa atribuída.", "failed_job_command": "O comando {command} falhou para a tarefa: {job}", "force_delete_user_warning": "AVISO: Isso removerá imediatamente o usuário e todos os arquivos. Isso não pode ser desfeito e os arquivos não podem ser recuperados.", "forcing_refresh_library_files": "Forçando a atualização de todos os arquivos da biblioteca", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Aceleração de hardware", "transcoding_hardware_acceleration_description": "Experimental; muito mais rápido, mas terá qualidade inferior com a mesma taxa de bits", "transcoding_hardware_decoding": "Decodificação de hardware", - "transcoding_hardware_decoding_setting_description": "Aplica-se apenas a NVENC, QSV e RKMPP. Permite aceleração ponta a ponta em vez de apenas acelerar a codificação. Pode não funcionar em todos os vídeos.", + "transcoding_hardware_decoding_setting_description": "Habilita a aceleração de ponta a ponta, em vez de apenas acelerar a codificação. Pode não funcionar em todos os vídeos.", "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Máximo de quadros B", "transcoding_max_b_frames_description": "Valores mais altos melhoram a eficiência da compactação, mas retardam a codificação. Pode não ser compatível com aceleração de hardware em dispositivos mais antigos. 0 desativa os quadros B, enquanto -1 define esse valor automaticamente.", @@ -888,6 +888,7 @@ "look": "Estilo", "loop_videos": "Repetir vídeos", "loop_videos_description": "Ative para repetir os vídeos automaticamente durante a exibição.", + "main_branch_warning": "Você está utilizando a versão de desenvolvimento. É altamente recomendado que utilize a versão estável!", "make": "Marca", "manage_shared_links": "Gerir links partilhados", "manage_sharing_with_partners": "Gerenciar compartilhamento com parceiros", @@ -1092,11 +1093,13 @@ "recent_searches": "Pesquisas recentes", "refresh": "Atualizar", "refresh_encoded_videos": "Atualizar vídeos codificados", + "refresh_faces": "Atualizar rostos", "refresh_metadata": "Atualizar metadados", "refresh_thumbnails": "Atualizar miniaturas", "refreshed": "Atualizado", "refreshes_every_file": "Atualiza todos arquivos", "refreshing_encoded_video": "Atualizando vídeo codificado", + "refreshing_faces": "Atualizando rostos", "refreshing_metadata": "Atualizando metadados", "regenerating_thumbnails": "Regenerando miniaturas", "remove": "Remover", diff --git a/web/src/lib/i18n/ro.json b/i18n/ro.json similarity index 100% rename from web/src/lib/i18n/ro.json rename to i18n/ro.json diff --git a/web/src/lib/i18n/ru.json b/i18n/ru.json similarity index 92% rename from web/src/lib/i18n/ru.json rename to i18n/ru.json index 35507d06c3..88026147cb 100644 --- a/web/src/lib/i18n/ru.json +++ b/i18n/ru.json @@ -28,7 +28,7 @@ "added_to_favorites_count": "Добавлено{count, number} в избранное", "admin": { "add_exclusion_pattern_description": "Добавьте шаблоны исключений. Подстановка с использованием *, ** и ? поддерживается. Чтобы игнорировать все файлы в любом каталоге с именем «Raw», используйте «**/Raw/**». Чтобы игнорировать все файлы, заканчивающиеся на «.tif», используйте «**/*.tif». Чтобы игнорировать абсолютный путь, используйте «/path/to/ignore/**».", - "asset_offline_description": "Этот внешний файл библиотеки больше не найден на диске и был перемещён в корзину. Если файл был перемещён внутри библиотеки, проверьте свою временную шкалу, чтобы найти новый соответствующий ресурс. Чтобы восстановить этот файл, убедитесь, что путь к файлу ниже доступен для Immich, и выполните сканирование библиотеки.", + "asset_offline_description": "Этот файл внешней библиотеки не был найден на диске и был перемещён в корзину. Если файл был перемещён внутри библиотеки, проверьте временную шкалу, чтобы найти новый соответствующий ресурс. Чтобы восстановить файл, убедитесь, что путь ниже доступен для Immich и выполните сканирование библиотеки.", "authentication_settings": "Настройки аутентификации", "authentication_settings_description": "Управление паролями, OAuth и другими настройками аутентификации", "authentication_settings_disable_all": "Вы уверены, что хотите отключить все методы входа? Вход будет полностью отключен.", @@ -38,7 +38,7 @@ "cleared_jobs": "Очищены задачи для: {job}", "config_set_by_file": "Настроено с помощью файла конфигурации", "confirm_delete_library": "Вы действительно хотите удалить библиотеку \"{library}\"?", - "confirm_delete_library_assets": "Вы уверены, что хотите удалить эту библиотеку? Это безвозвратно удалит {count, plural, one {# содержимый объект} few {# содержимых объекта} other {all # содержимых объектов}} с Immich. Файлы останутся на диске.", + "confirm_delete_library_assets": "Вы уверены, что хотите удалить эту библиотеку? Это безвозвратно удалит {count, plural, one {# содержимый объект} few {# содержимых объекта} other {all # содержимых объектов}} из Immich. Файлы останутся на диске.", "confirm_email_below": "Чтобы подтвердить, введите \"{email}\" ниже", "confirm_reprocess_all_faces": "Вы уверены, что хотите повторно определить все лица? Будут также удалены имена со всех лиц.", "confirm_user_password_reset": "Вы уверены, что хотите сбросить пароль пользователя {user}?", @@ -51,10 +51,10 @@ "external_library_created_at": "Внешняя библиотека (создана {date})", "external_library_management": "Управление внешними библиотеками", "face_detection": "Обнаружение лиц", - "face_detection_description": "Обнаруживает лица на ресурсах с помощью машинного обучения. Для видео учитывается только миниатюра. “Обновить” - обрабатывает все ресурсы. “Сброс” также удаляет все имеющиеся данные лиц. “Пропущенные” - в очередь помещаются только не обработанные ресурсы. Обнаруженные лица будут помещены в очередь для распознавания лиц после завершения обнаружения лиц, объединяя их в существующие или новые группы людей.", + "face_detection_description": "Обнаруживает лица на медиа с помощью машинного обучения. Для видео учитывается только миниатюра. “Обновить” — обработать все медиа. “Сброс” — удалить все имеющиеся данные лиц и обработать заново. “Пропущенные” — добавить в очередь необработанные медиа. Обнаруженные лица будут помещены в очередь распознавания для привязки к существующим или новым людям.", "facial_recognition_job_description": "Группирует распознанные лица по людям. Этот шаг выполняется после завершения обнаружения лиц. “Сброс” - группирует все лица. “Пропущенные” - помещает в очередь лица, не привязанные к человеку.", "failed_job_command": "Команда {command} не выполнена для задачи: {job}", - "force_delete_user_warning": "ПРЕДУПРЕЖДЕНИЕ: Это приведет к немедленному удалению пользователя и всех ресурсов. Это невозможно отменить, и файлы не могут быть восстановлены.", + "force_delete_user_warning": "ПРЕДУПРЕЖДЕНИЕ: Это приведет к немедленному удалению пользователя и его ресурсов. Это действие невозможно отменить, и файлы не могут быть восстановлены.", "forcing_refresh_library_files": "Принудительное обновление всех файлов библиотеки", "image_format": "Формат", "image_format_description": "WebP создает файлы меньшего размера, чем JPEG, но кодирует медленнее.", @@ -62,7 +62,7 @@ "image_prefer_embedded_preview_setting_description": "Используйте встроенные превью в фотографиях RAW в качестве входных данных для обработки изображений, если они доступны. Это может обеспечить более точную цветопередачу для некоторых изображений, но качество предварительного просмотра зависит от камеры, и изображение может иметь больше артефактов сжатия.", "image_prefer_wide_gamut": "Предпочитаю широкую гамму", "image_prefer_wide_gamut_setting_description": "Используйте Display P3 для миниатюр. Это лучше сохраняет яркость изображений с широким цветовым пространством, но изображения могут выглядеть по-другому на старых устройствах со старой версией браузера. Изображения sRGB сохраняются в формате sRGB, что позволяет избежать цветовых сдвигов.", - "image_preview_description": "Изображение среднего размера с разделенными метаданными, используемое при просмотре отдельного объекта и для машинного обучения", + "image_preview_description": "Изображение среднего размера без метаданных, используемое при отдельном просмотре и для машинного обучения", "image_preview_format": "Формат превью", "image_preview_quality_description": "Качество предварительного просмотра от 1 до 100. Чем выше, тем лучше, но при этом создаются файлы большего размера и может снизиться скорость отклика приложения. Установка низкого значения может повлиять на качество машинного обучения.", "image_preview_resolution": "Разрешение превью", @@ -110,7 +110,7 @@ "machine_learning_clip_model_description": "Названия моделей CLIP размещены здесь. Обратите внимание, что при изменении модели необходимо заново запустить задачу «Интеллектуальный поиск» для всех изображений.", "machine_learning_duplicate_detection": "Поиск дубликатов", "machine_learning_duplicate_detection_enabled": "Включить обнаружение дубликатов", - "machine_learning_duplicate_detection_enabled_description": "Если этот параметр отключен, абсолютно идентичные ресурсы всё равно будут удалены из дубликатов.", + "machine_learning_duplicate_detection_enabled_description": "Если этот параметр отключен, абсолютно идентичные файлы всё равно будут удалены из дубликатов.", "machine_learning_duplicate_detection_setting_description": "Используйте встраивания CLIP для поиска вероятных дубликатов", "machine_learning_enabled": "Включите машинное обучение", "machine_learning_enabled_description": "При отключении, все функции ML будут отключены независимо от следующих параметров.", @@ -151,16 +151,16 @@ "map_settings_description": "Управление настройками карты", "map_style_description": "URL-адрес темы карты style.json", "metadata_extraction_job": "Извлечение метаданных", - "metadata_extraction_job_description": "Извлекает метаданные из каждого ресурса, такие как координаты GPS и разрешение", + "metadata_extraction_job_description": "Извлекает метаданные из каждого файла, такие как местоположение, лица и разрешение", "metadata_faces_import_setting": "Включить импорт лиц", "metadata_faces_import_setting_description": "Импорт лиц из изображений EXIF-данных и файлов sidecar", "metadata_settings": "Настройки метаданных", "metadata_settings_description": "Управление настройками метаданных", "migration_job": "Миграция", - "migration_job_description": "Выполняет перенос миниатюр для ресурсов и лиц в последнюю структуру папок", + "migration_job_description": "Выполняет перенос миниатюр ресурсов и лиц в последнюю структуру папок", "no_paths_added": "Пути не добавлены", "no_pattern_added": "Шаблон не добавлен", - "note_apply_storage_label_previous_assets": "Примечание: Чтобы применить тег хранилища к ранее загруженным ресурсам запустите", + "note_apply_storage_label_previous_assets": "Примечание: Чтобы применить тег хранилища к ранее загруженным ресурсам, запустите", "note_cannot_be_changed_later": "ПРИМЕЧАНИЕ: Это невозможно изменить позже!", "note_unlimited_quota": "Примечание: Введите 0 для неограниченной квоты или оставьте пустым", "notification_email_from_address": "Адрес отправителя", @@ -236,8 +236,8 @@ "sidecar_job": "Метаданные из sidecar-файлов", "sidecar_job_description": "Обнаруживает и синхронизирует метаданные из sidecar-файлов", "slideshow_duration_description": "Количество секунд для отображения каждого изображения", - "smart_search_job_description": "Запускает машинное обучение на объектах для поддержки умного поиска", - "storage_template_date_time_description": "Время создание объекта использовано как информация о времени съемки", + "smart_search_job_description": "Распознает содержимое медиафайлов для умного поиска", + "storage_template_date_time_description": "Время создания файла использовано как информация о времени съемки", "storage_template_date_time_sample": "Время выборки {date}", "storage_template_enable_description": "Включить механизм шаблонов хранилища", "storage_template_hash_verification_enabled": "Включить проверку хеша", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Аппаратное ускорение", "transcoding_hardware_acceleration_description": "Экспериментальный; намного быстрее, но будет иметь более низкое качество при том же битрейте", "transcoding_hardware_decoding": "Аппаратное декодирование", - "transcoding_hardware_decoding_setting_description": "Применяется только к NVENC и RKMPP. Включает сквозное ускорение, а не только ускорение кодирования. Может работать не со всеми видео.", + "transcoding_hardware_decoding_setting_description": "Включает сквозное ускорение, а не только ускорение кодирования. Может работать не со всеми видео.", "transcoding_hevc_codec": "Кодек HEVC", "transcoding_max_b_frames": "Максимально промежуточных кадров", "transcoding_max_b_frames_description": "Более высокие значения повышают эффективность сжатия, но замедляют кодирование. Может быть несовместимо с аппаратным ускорением на старых устройствах. 0 отключает B-кадры, а -1 устанавливает это значение автоматически.", @@ -323,17 +323,17 @@ "transcoding_video_codec_description": "VP9 обладает высокой эффективностью и веб-совместимостью, но перекодирование занимает больше времени. HEVC работает аналогично, но имеет меньшую веб-совместимость. H.264 широко совместим и быстро перекодируется, но создает файлы гораздо большего размера. AV1 — наиболее эффективный кодек, но он не поддерживается на старых устройствах.", "trash_enabled_description": "Включить корзину", "trash_number_of_days": "Срок хранения", - "trash_number_of_days_description": "Количество дней, в течение которых объекты будут храниться в корзине, прежде чем они будут окончательно удалены", + "trash_number_of_days_description": "Количество дней, в течение которых файлы будут храниться в корзине до окончательного удаления", "trash_settings": "Настройки корзины", "trash_settings_description": "Управление настройками корзины", "untracked_files": "НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ", "untracked_files_description": "Приложение не отслеживает эти файлы. Они могут быть результатом неудачных перемещений, прерванных загрузок или пропущены из-за ошибки", "user_cleanup_job": "Очистка пользователя", - "user_delete_delay": "Аккаунт и ресурсы пользователя {user} будут запланированы для окончательного удаления через {delay, plural, one {# день} few {# дня} many {# дней} other {# дня}}.", + "user_delete_delay": "Аккаунт и файлы пользователя {user} будут отложены до окончательного удаления через {delay, plural, one {# день} few {# дня} many {# дней} other {# дня}}.", "user_delete_delay_settings": "Отложенное удаление", - "user_delete_delay_settings_description": "Срок в днях, по истечение которого происходит окончательное удаление учетной записи пользователя и его ресурсов после удаления учётной записи. Задача по удалению пользователей выполняется в полночь. Изменения этой настройки будут учтены при следующем запуске задачи.", - "user_delete_immediately": "Аккаунт и ресурсы пользователя {user} будут поставлены в очередь на немедленное окончательное удаление.", - "user_delete_immediately_checkbox": "Поставить пользователя и объекты в очередь для удаления", + "user_delete_delay_settings_description": "Срок в днях, по истечение которого происходит окончательное удаление учетной записи пользователя и его ресурсов. Задача по удалению пользователей выполняется в полночь. Изменения этой настройки будут учтены при следующем запуске задачи.", + "user_delete_immediately": "Аккаунт и файлы пользователя {user} будут немедленно поставлены в очередь для окончательного удаления.", + "user_delete_immediately_checkbox": "Поместить пользователя и его файлы в очередь для немедленного удаления", "user_management": "Управление пользователями", "user_password_has_been_reset": "Пароль пользователя был сброшен:", "user_password_reset_description": "Пожалуйста, предоставьте временный пароль пользователю и сообщите ему, что при следующем входе в систему пароль нужно будет изменить.", @@ -370,12 +370,12 @@ "album_remove_user_confirmation": "Вы уверены, что хотите удалить пользователя {user}?", "album_share_no_users": "Похоже, вы поделились этим альбомом со всеми пользователями или у вас нет пользователей, с которыми можно поделиться.", "album_updated": "Альбом обновлён", - "album_updated_setting_description": "Получать уведомление по электронной почте, когда в общий альбом добавлены новые ресурсы", + "album_updated_setting_description": "Получать уведомление по электронной почте при добавлении новых ресурсов в общий альбом", "album_user_left": "Вы покинули {album}", "album_user_removed": "Пользователь {user} удален", "album_with_link_access": "Поделитесь ссылкой на альбом, чтобы ваши друзья могли его посмотреть.", "albums": "Альбомы", - "albums_count": "{count, plural, one {Альбом ({count, number})} few {Альбома ({count, number})} many {Альбомов ({count, number})} other {Альбомов ({count, number})}}", + "albums_count": "{count, plural, one {{count, number} альбом} few {{count, number} альбома} many {{count, number} альбомов} other {{count, number} альбомов}}", "all": "Все", "all_albums": "Все альбомы", "all_people": "Все люди", @@ -401,20 +401,20 @@ "are_you_sure_to_do_this": "Вы уверены, что хотите это сделать?", "asset_added_to_album": "Добавлено в альбом", "asset_adding_to_album": "Добавление в альбом...", - "asset_description_updated": "Описание ресурса было обновлено", + "asset_description_updated": "Описание обновлено", "asset_filename_is_offline": "Объект {filename} находится в офлайн-режиме", "asset_has_unassigned_faces": "Есть не распознанные лица", "asset_hashing": "Хеширование...", "asset_offline": "Объект отключён", - "asset_offline_description": "Этот внешний объект больше не найден на диске. Пожалуйста, свяжитесь с администратором Immich для получения помощи.", + "asset_offline_description": "Этот внешний файл не найден на диске. Пожалуйста, свяжитесь с администратором Immich для получения помощи.", "asset_skipped": "Пропущено", "asset_skipped_in_trash": "В корзине", "asset_uploaded": "Загружено", "asset_uploading": "Загрузка...", "assets": "Объекты", "assets_added_count": "Добавлено {count, plural, one {# объект} few {# объекта} other {# объектов}}", - "assets_added_to_album_count": "Добавлено {count, plural, one {# объект} few {# объекта} other {# объектов}} в альбом", - "assets_added_to_name_count": "Добавлено {count, plural, one {# объект} other {# объектов}} в {hasName, select, true {{name}} other {новый альбом}}", + "assets_added_to_album_count": "В альбом добавлено {count, plural, one {# объект} few {# объекта} other {# объектов}}", + "assets_added_to_name_count": "Добавлено {count, plural, one {# объект} few {# объекта} other {# объектов}} в {hasName, select, true {{name}} other {новый альбом}}", "assets_count": "{count, plural, one {# объект} few {# объекта} other {# объектов}}", "assets_moved_to_trash": "Перемещено {count, plural, one {# объект} few {# объекта} many {# объектов} other {# объекта}} в корзину", "assets_moved_to_trash_count": "{count, plural, one {# объект} few {# объекта} other {# объектов}} перемещено в корзину", @@ -423,7 +423,7 @@ "assets_restore_confirmation": "Вы уверены, что хотите восстановить все объекты из корзины? Это действие нельзя отменить! Обратите внимание, что любые оффлайн-объекты не могут быть восстановлены таким способом.", "assets_restored_count": "{count, plural, one {# объект} few {# объекта} other {# объектов}} восстановлено", "assets_trashed_count": "{count, plural, one {# объект} few {# объекта} other {# объектов}} перемещено в корзину", - "assets_were_part_of_album_count": "{count, plural, one {# Объект} other {# Объекты}} уже часть альбома", + "assets_were_part_of_album_count": "{count, plural, one {# объект} few {# объекта} other {# объектов}} уже в альбоме", "authorized_devices": "Разрешенные устройства", "back": "Назад", "back_close_deselect": "Назад, закрыть или отменить выбор", @@ -431,12 +431,12 @@ "birthdate_saved": "Дата рождения успешно сохранена", "birthdate_set_description": "Дата рождения используется для расчета возраста этого человека на момент фотографии.", "blurred_background": "Размытый фон", - "bugs_and_feature_requests": "Ошибки и предложения", + "bugs_and_feature_requests": "Ошибки и запросы", "build": "Сборка", "build_image": "Версия сборки", - "bulk_delete_duplicates_confirmation": "Вы уверены, что хотите массово удалить {count, plural, one {# дублирующийся ресурс} other {# дублирующихся ресурсов}}? Это сохранит самый большой ресурс из каждой группы и навсегда удалит все остальные дубликаты. Это действие нельзя отменить!", - "bulk_keep_duplicates_confirmation": "Вы уверены, что хотите оставить {count, plural, one {# дублирующийся ресурс} other {# дублирующихся ресурсов}}? Это разрешит все группы дубликатов без удаления чего-либо.", - "bulk_trash_duplicates_confirmation": "Вы уверены, что хотите массово переместить в корзину {count, plural, one {# дублирующийся ресурс} other {# дублирующихся ресурсов}}? Это сохранит самый большой ресурс из каждой группы и переместит в корзину все остальные дубликаты.", + "bulk_delete_duplicates_confirmation": "Вы уверены, что хотите массово удалить {count, plural, one {# дублирующийся объект} other {# дублирующихся объектов}}? Это сохранит самый большой файл из каждой группы и навсегда удалит дубликаты. Это действие нельзя отменить!", + "bulk_keep_duplicates_confirmation": "Вы уверены, что хотите оставить {count, plural, one {# дублирующийся объект} other {# дублирующихся объектов}}? Это сохранит все дубликаты.", + "bulk_trash_duplicates_confirmation": "Вы уверены, что хотите массово переместить в корзину {count, plural, one {# дублирующийся объект} other {# дублирующихся объектов}}? Это сохранит самый большой файл из каждой группы и переместит дубликаты в корзину.", "buy": "Приобретение лицензии Immich", "camera": "Камера", "camera_brand": "Производитель", @@ -548,7 +548,7 @@ "display_options": "Настройки отображения", "display_order": "Порядок отображения", "display_original_photos": "Отображение оригинальных фотографий", - "display_original_photos_setting_description": "Предпочитать отображать исходную фотографию при просмотре ресурса, а не миниатюры, если исходный ресурс совместим с Интернетом. Это может привести к снижению скорости отображения фотографий.", + "display_original_photos_setting_description": "Предпочитать исходную фотографию при просмотре ресурса вместо миниатюры, если исходный ресурс поддерживается. Это может снизить скорости отображения фотографий.", "do_not_show_again": "Не показывать это сообщение в дальнейшем", "documentation": "Документация", "done": "Готово", @@ -605,11 +605,11 @@ "error_loading_image": "Ошибка при загрузке изображения", "error_title": "Ошибка - Что-то пошло не так", "errors": { - "cannot_navigate_next_asset": "Невозможно перейти к следующему объекту", - "cannot_navigate_previous_asset": "Не удается перейти к предыдущему ресурсу", + "cannot_navigate_next_asset": "Не удалось перейти к следующему объекту", + "cannot_navigate_previous_asset": "Не удалось перейти к предыдущему ресурсу", "cant_apply_changes": "Не удается применить изменения", "cant_change_activity": "Не удается {enabled, select, true {отключить} other {включить}} активность", - "cant_change_asset_favorite": "Не удается изменить статус \"избранное\" для ресурса", + "cant_change_asset_favorite": "Не удалось изменить статус \"избранное\" для ресурса", "cant_change_metadata_assets_count": "Не удается изменить метаданные у {count, plural, one {# ресурса} few {# ресурсов} many {# ресурсов} other {# ресурсов}}", "cant_get_faces": "Не удается получить лица", "cant_get_number_of_comments": "Не удается получить количество комментариев", @@ -633,8 +633,8 @@ "failed_to_load_assets": "Ошибка загрузки объектов", "failed_to_load_people": "Не удалось загрузить людей", "failed_to_remove_product_key": "Не удалось удалить ключ продукта", - "failed_to_stack_assets": "Не удалось создать стек", - "failed_to_unstack_assets": "Не удалось разобрать стек", + "failed_to_stack_assets": "Не удалось сгруппировать объекты", + "failed_to_unstack_assets": "Не удалось разгруппировать объекты", "import_path_already_exists": "Этот путь импорта уже существует.", "incorrect_email_or_password": "Неверный адрес электронной почты или пароль", "paths_validation_failed": "{paths, plural, one {# путь} other {# путей}} не прошли проверку", @@ -647,8 +647,8 @@ "unable_to_add_exclusion_pattern": "Невозможно добавить шаблон исключения", "unable_to_add_import_path": "Не удается добавить путь импорта", "unable_to_add_partners": "Невозможно добавить партнеров", - "unable_to_add_remove_archive": "Не удалось {archived, select, true {удалить ресурс из} other {добавить ресурс в}} архив", - "unable_to_add_remove_favorites": "Не удалось {favorite, select, true {добавить ресурс в} other {удалить ресурс из}} избранного", + "unable_to_add_remove_archive": "Не удалось {archived, select, true {удалить ресурс из архива} other {добавить ресурс в архив}}", + "unable_to_add_remove_favorites": "Не удалось {favorite, select, true {добавить ресурс в избранное} other {удалить ресурс из избранного}}", "unable_to_archive_unarchive": "Не удалось {archived, select, true {архивировать} other {разархивировать}}", "unable_to_change_album_user_role": "Не удалось изменить роль пользователя в альбоме", "unable_to_change_date": "Невозможно изменить дату", @@ -667,7 +667,7 @@ "unable_to_create_library": "Не удалось создать библиотеку", "unable_to_create_user": "Не удалось создать пользователя", "unable_to_delete_album": "Не удается удалить альбом", - "unable_to_delete_asset": "Не удается удалить ресурс", + "unable_to_delete_asset": "Не удалось удалить ресурс", "unable_to_delete_assets": "Ошибка при удалении ресурсов", "unable_to_delete_exclusion_pattern": "Не удается удалить шаблон исключения", "unable_to_delete_import_path": "Не удается удалить путь импорта", @@ -685,7 +685,7 @@ "unable_to_link_motion_video": "Не удается связать движущееся видео", "unable_to_link_oauth_account": "Не удается связать учетную запись OAuth", "unable_to_load_album": "Невозможно загрузить альбом", - "unable_to_load_asset_activity": "Не удалось загрузить активность объекта", + "unable_to_load_asset_activity": "Не удалось загрузить комментарии", "unable_to_load_items": "Не удалось загрузить элементы", "unable_to_load_liked_status": "Невозможно загрузить статус лайка", "unable_to_log_out_all_devices": "Невозможно выйти из всех устройств", @@ -695,7 +695,7 @@ "unable_to_reassign_assets_existing_person": "Невозможно переназначить ресурсы на {name, select, null {существующего человека} other {{name}}}", "unable_to_reassign_assets_new_person": "Не удается переназначить ресурсы новому человеку", "unable_to_refresh_user": "Невозможно обновить пользователя", - "unable_to_remove_album_users": "Не удается удалить пользователей из альбома", + "unable_to_remove_album_users": "Не удалось удалить пользователей из альбома", "unable_to_remove_api_key": "Не удается удалить ключ API", "unable_to_remove_assets_from_shared_link": "Невозможно удалить объекты из общей ссылки", "unable_to_remove_comment": "", @@ -721,10 +721,10 @@ "unable_to_set_feature_photo": "Не удалось установить фотографию на обложку", "unable_to_set_profile_picture": "Невозможно установить изображение профиля", "unable_to_submit_job": "Невозможно отправить задание", - "unable_to_trash_asset": "Невозможно удалить актив", + "unable_to_trash_asset": "Не удалось переместить объект в корзину", "unable_to_unlink_account": "Не удалось отсоединить учетную запись", "unable_to_unlink_motion_video": "Не удается отсоединить движущееся видео", - "unable_to_update_album_cover": "Невозможно обновить обложку альбома", + "unable_to_update_album_cover": "Не удалось обновить обложку альбома", "unable_to_update_album_info": "Невозможно обновить информацию об альбоме", "unable_to_update_library": "Не удалось обновить библиотеку", "unable_to_update_location": "Не удалось обновить местоположение", @@ -743,7 +743,7 @@ "expire_after": "Истекает через", "expired": "Срок действия истек", "expires_date": "Срок действия до {date}", - "explore": "Просмотр", + "explore": "Поиск", "explorer": "Проводник", "export": "Экспортировать", "export_as_json": "Экспорт в JSON", @@ -815,7 +815,7 @@ "in_archive": "В архиве", "include_archived": "Отображать архив", "include_shared_albums": "Включать общие альбомы", - "include_shared_partner_assets": "Включать общие активы партнеров", + "include_shared_partner_assets": "Включать общие ресурсы партнера", "individual_share": "Персональный доступ", "info": "Информация", "interval": { @@ -888,6 +888,7 @@ "look": "Просмотр", "loop_videos": "Циклическое воспроизведение", "loop_videos_description": "Включить циклическое воспроизведение видео.", + "main_branch_warning": "Вы используете версию для разработки; мы настоятельно рекомендуем использовать релизную версию!", "make": "Производитель", "manage_shared_links": "Управление общими ссылками", "manage_sharing_with_partners": "Управление обменом информацией с партнерами. Эта функция позволяет вашему партнеру видеть ваши фотографии и видеозаписи, кроме тех, которые находятся в Архиве и Корзине", @@ -950,7 +951,7 @@ "no_results_description": "Попробуйте использовать синоним или более общее ключевое слово", "no_shared_albums_message": "Создайте альбом для обмена фотографиями и видеозаписями с людьми в вашей сети", "not_in_any_album": "Ни в одном альбоме", - "note_apply_storage_label_to_previously_uploaded assets": "Примечание: Чтобы применить тег хранилища к ранее загруженным ресурсам запустите", + "note_apply_storage_label_to_previously_uploaded assets": "Примечание: Чтобы применить тег хранилища к ранее загруженным ресурсам, запустите", "note_unlimited_quota": "Примечание: Введите 0 для неограниченной квоты", "notes": "Примечание", "notification_toggle_setting_description": "Включить уведомления по электронной почте", @@ -1010,19 +1011,19 @@ "people_sidebar_description": "Отображать пункт меню \"Люди\" в боковой панели", "perform_library_tasks": "", "permanent_deletion_warning": "Предупреждение об удалении", - "permanent_deletion_warning_setting_description": "Отображать предупреждение при безвозвратном удалении ресурсов", + "permanent_deletion_warning_setting_description": "Предупреждать перед безвозвратным удалением ресурсов", "permanently_delete": "Удалить навсегда", - "permanently_delete_assets_count": "Полностью удалить {count, plural, one {ресурс} few {ресурса} many {ресурсов} other {ресурсов}}", + "permanently_delete_assets_count": "Безвозвратно удалить {count, plural, one {ресурс} few {ресурса} many {ресурсов} other {ресурсов}}", "permanently_delete_assets_prompt": "Вы действительно хотите навсегда удалить {count, plural, one {этот объект?} other {эти # объектов?}} Это так же удалит {count, plural, one {его} other {их}} из альбома(ов).", - "permanently_deleted_asset": "Удалить объект навсегда", + "permanently_deleted_asset": "Удалить навсегда", "permanently_deleted_assets": "Безвозвратно удалено {count, plural, one {# ресурс} few {# ресурса} many {# ресурсов} other {# ресурса}}", - "permanently_deleted_assets_count": "Полностью удалено {count, plural, one {# ресурс} few {# ресурса} many {# ресурсов} other {# ресурса}}", + "permanently_deleted_assets_count": "Безвозвратно удалено {count, plural, one {# файл} few {# файла} many {# файлов} other {# файлов}}", "person": "Человек", "person_hidden": "{name}{hidden, select, true { (скрыт)} other {}}", "photo_shared_all_users": "Похоже, что вы поделились своими фотографиями со всеми пользователями или у вас нет пользователей, с которыми можно поделиться.", "photos": "Фото", "photos_and_videos": "Фото и Видео", - "photos_count": "{count, plural, one {Фотография ({count, number})} few {Фотографии ({count, number})} many {Фотографий ({count, number})} other {Фотографий ({count, number})}}", + "photos_count": "{count, plural, one {{count, number} Фотография} few {{count, number} Фотографии} many {{count, number} Фотографий} other {{count, number} Фотографий}}", "photos_from_previous_years": "Фотографии прошлых лет в этот день", "pick_a_location": "Выбрать местоположение", "place": "Места", @@ -1085,9 +1086,9 @@ "reaction_options": "Опции реакций", "read_changelog": "Прочитать список изменений", "reassign": "Переназначить", - "reassigned_assets_to_existing_person": "Переназначено {count, plural, one {# ресурс} few {# ресурса} many {# ресурсов} other {# ресурса}} на {name, select, null {существующего человека} other {{name}}}", - "reassigned_assets_to_new_person": "Переназначено {count, plural, one {# ресурс} few {# ресурса} many {# ресурсов} other {# ресурса}} новому человеку", - "reassing_hint": "Назначить выбранные ресурсы существующему человеку", + "reassigned_assets_to_existing_person": "Переназначен{count, plural, one { # ресурс} few {о # ресурса} many {о # ресурсов} other {о # ресурсов}} на {name, select, null {существующего пользователя} other {{name}}}", + "reassigned_assets_to_new_person": "Переназначен{count, plural, one { # ресурс} few {о # ресурса} many {о # ресурсов} other {о # ресурсов}} новому человеку", + "reassing_hint": "Назначить выбранные ресурсы существующему пользователю", "recent": "Недавние", "recent_searches": "Недавние поисковые запросы", "refresh": "Обновить", @@ -1102,8 +1103,8 @@ "refreshing_metadata": "Обновление метаданных", "regenerating_thumbnails": "Восстановление миниатюр", "remove": "Удалить", - "remove_assets_album_confirmation": "Вы уверены, что хотите удалить {count, plural, one {# ресурс} few {# ресурса} many {# ресурсов} other {# ресурса}} из альбома?", - "remove_assets_shared_link_confirmation": "Вы уверены, что хотите удалить {count, plural, one {# ресурс} few {# ресурса} many {# ресурсов} other {# ресурса}} из этой общей ссылки?", + "remove_assets_album_confirmation": "Вы уверены, что хотите удалить {count, plural, one {# объект} few {# объекта} many {# объектов} other {# объектов}} из альбома?", + "remove_assets_shared_link_confirmation": "Вы уверены, что хотите удалить {count, plural, one {# объект} few {# объекта} many {# объектов} other {# объектов}} из этого общего доступа?", "remove_assets_title": "Удалить объекты?", "remove_custom_date_range": "Удалить пользовательский диапазон дат", "remove_deleted_assets": "Удаление автономных файлов", @@ -1212,7 +1213,7 @@ "shared_from_partner": "Фото от {partner}", "shared_link_options": "Параметры общих ссылок", "shared_links": "Общие ссылки", - "shared_photos_and_videos_count": "{assetCount, plural, other {# поделился фото и видео.}}", + "shared_photos_and_videos_count": "{assetCount, plural, other {# фото и видео.}}", "shared_with_partner": "Совместно с {partner}", "sharing": "Общие", "sharing_enter_password": "Пожалуйста, введите пароль для просмотра этой страницы.", @@ -1311,7 +1312,7 @@ "trash": "Корзина", "trash_all": "Удалить всё", "trash_count": "Удалить {count, number}", - "trash_delete_asset": "Удалить ресурс", + "trash_delete_asset": "Переместить в корзину", "trash_no_results_message": "Здесь будут отображаться удалённые фотографии и видео.", "trashed_items_will_be_permanently_deleted_after": "Элементы в корзине будут автоматически удалены через {days, plural, one {# день} other {# дней}}.", "type": "Тип", @@ -1334,16 +1335,16 @@ "unselect_all": "Снять всё", "unselect_all_duplicates": "Отменить выбор всех дубликатов", "unstack": "Разобрать стек", - "unstacked_assets_count": "{count, plural, one {# объект} few {# объекта} other {# объектов}} разобрано из стека", + "unstacked_assets_count": "{count, plural, one {# объект извлечен} few {# объекта извлечено} other {# объектов извлечено}} из стека", "untracked_files": "НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ", "untracked_files_decription": "Приложение не отслеживает эти файлы. Они могут быть результатом неудачных перемещений, прерванных загрузок или пропущены из-за ошибки", "up_next": "Следующее", "updated_password": "Пароль обновлён", "upload": "Загрузить", "upload_concurrency": "Параллельность загрузки", - "upload_errors": "Загрузка завершена с {count, plural, one {# ошибкой} few {# ошибками} many {# ошибками} other {# ошибками}}, обновите страницу, чтобы увидеть новые загруженные ресурсы.", + "upload_errors": "Загрузка завершена с {count, plural, one {# ошибкой} other {# ошибками}}, обновите страницу, чтобы увидеть новые загруженные ресурсы.", "upload_progress": "Осталось {remaining, number} - Обработано {processed, number}/{total, number}", - "upload_skipped_duplicates": "Пропущено {count, plural, one {# дублирующийся ресурс} few {# дублирующихся ресурса} many {# дублирующихся ресурсов} other {# дублирующихся ресурса}}", + "upload_skipped_duplicates": "Пропущен{count, plural, one { # дублирующийся ресурс} few {о # дублирующихся ресурса} many {о # дублирующихся ресурсов} other {о # дублирующихся ресурса}}", "upload_status_duplicates": "Дубликаты", "upload_status_errors": "Ошибки", "upload_status_uploaded": "Загружено", @@ -1367,14 +1368,14 @@ "variables": "Переменные", "version": "Версия", "version_announcement_closing": "Твой друг Алекс", - "version_announcement_message": "Привет, друг! В приложении доступна новая версия. Пожалуйста, посетите заметки к выпуску и убедитесь, что ваша настройка docker-compose.yml и .env актуальна, чтобы избежать ошибок конфигурации, особенно если вы используете WatchTower или другой механизм автоматического обновления вашего приложения.", + "version_announcement_message": "Привет, друг! Доступна новая версия приложения. Пожалуйста, посетите заметки к выпуску и убедитесь, что ваши параметры docker-compose.yml и .env актуальны, чтобы избежать ошибок конфигурации, особенно если вы используете WatchTower или другой механизм автоматического обновления приложения.", "version_history": "История версий", "version_history_item": "Версия {version} установлена {date}", "video": "Видео", "video_hover_setting": "Воспроизведение миниатюры видео при наведении курсора мыши", "video_hover_setting_description": "Воспроизводить миниатюры видео при наведении курсора мыши на объект. Даже если этот параметр отключен, воспроизведение можно запустить, наведя курсор на значок воспроизведения.", "videos": "Видео", - "videos_count": "{count, plural, one {Видео (#)} few {Видео (#)} many {Видео (#)} other {Видео (#)}}", + "videos_count": "{count, plural, one {# Видео} few {# Видео} many {# Видео} other {# Видео}}", "view": "Просмотр", "view_album": "Просмотреть альбом", "view_all": "Посмотреть всё", diff --git a/web/src/lib/i18n/sk.json b/i18n/sk.json similarity index 51% rename from web/src/lib/i18n/sk.json rename to i18n/sk.json index 65020a260c..1d0cd834fc 100644 --- a/web/src/lib/i18n/sk.json +++ b/i18n/sk.json @@ -7,14 +7,14 @@ "actions": "Akcie", "active": "Aktívny", "activity": "Aktivita", - "activity_changed": "Aktivita je {enabled, select, true{povolená} other {zakázaná}}", + "activity_changed": "Aktivita je {enabled, select, true{povolená} other {vypnutá}}", "add": "Pridať", "add_a_description": "Pridať popis", "add_a_location": "Pridať polohu", "add_a_name": "Pridať meno", "add_a_title": "Pridať názov", "add_exclusion_pattern": "Pridať vzor vylúčenia", - "add_import_path": "Pridať cestu importu", + "add_import_path": "Pridať cestu pre import", "add_location": "Pridať lokáciu", "add_more_users": "Pridať viac používateľov", "add_partner": "Pridať partnera", @@ -27,128 +27,162 @@ "added_to_favorites": "Pridané do obľúbených", "added_to_favorites_count": "Pridané {count, number} do obľúbených", "admin": { - "authentication_settings": "Nastavenia overenia", + "add_exclusion_pattern_description": "Pridávanie vzorov na vylúčenie. Globovanie pomocou *, ** a ? je podporované. Ak chcete ignorovať všetky súbory v akomkoľvek adresári s názvom \"Raw\", použite \"**/Raw/**\". Ak chcete ignorovať všetky súbory končiace na \".tif\", použite \"**/*.tif\". Ak chcete ignorovať absolútnu cestu, použite príkaz \"/cesta/k/ignorovanym/**\".", + "asset_offline_description": "Táto položka externej knižnice sa už na disku nenachádza a bola presunutá do koša. Pokiaľ bol súbor presunutý v rámci knižnice, skontrolujte časovú os a vyhľadajte nové odpovedajúce položky. Ak chcete túto položku obnoviť, uistite sa, že je cesta k nižšie uvedenému súboru prístupná pre aplikáciu Immich a prehľadajte knižnicu.", + "authentication_settings": "Nastavenia overovania", "authentication_settings_description": "Spravovať heslo, protokol OAuth a ďalšie nastavenia overenia", "authentication_settings_disable_all": "Naozaj chcete zakázať všetky spôsoby prihlásenia? Prihlásenie bude úplne zakázané.", - "authentication_settings_reenable": "Pre povolenie použite Serverový príkaz.", + "authentication_settings_reenable": "Pre opätovné povolenie použite Serverový príkaz.", "background_task_job": "Úlohy na pozadí", "check_all": "Skontrolovať všetko", + "cleared_jobs": "Hotové úlohy pre: {job}", "config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom", "confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?", - "confirm_email_below": "Pre potvrdenie zadajte nižšie \"{email}\"", + "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" nižšie", "confirm_reprocess_all_faces": "Naozaj chcete spracovať všetky tváre znova? Tento proces vymaže pomenovaných ľudí.", - "confirm_user_password_reset": "Určite chcete resetovať heslo pre {user}?", + "confirm_user_password_reset": "Naozaj chcete resetovať heslo pre {user}?", + "create_job": "Vytvoriť úlohu", "crontab_guru": "", "disable_login": "Zakázať prihlásenie", "disabled": "", "duplicate_detection_job_description": "Spustiť strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", + "exclusion_pattern_description": "Vylučovacie vzory Vám umožňujú ignorovať súbory a priečinky pri skenovaní Vašej knižnice. Toto je užitočné, ak máte priečinky obsahujúce súbory, ktoré nechcete importovať, napríklad RAW súbory.", "external_library_created_at": "Externá knižnica (vytvorená {date})", "external_library_management": "Správa Externej Knižnice", - "face_detection": "Detekcia tváre", - "force_delete_user_warning": "VAROVANIE: Toto okamžite zmaže užívateľa a všetky súbory. Táto akcia sa nedá zvrátiť a súbory sa už nebudú dať vrátiť späť.", + "face_detection": "Detekcia tvárí", + "face_detection_description": "Detekujte tváre v položkách pomocou strojového učenia. Pri videách sa berie do úvahy iba miniatúra. „Obnoviť“ znovu spracuje všetky položky. „Resetovať“ navyše vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí položky, ktoré ešte neboli spracované. Detekované tváre budú zaradené na rozpoznávanie tvárí po dokončení detekcie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", + "facial_recognition_job_description": "Zoskupovať detekované tváre do osôb. Tento krok sa vykoná po dokončení detekcie tvárí. „Resetovať“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", + "failed_job_command": "Príkaz {command} zlyhal pre úlohu: {job}", + "force_delete_user_warning": "VAROVANIE: Toto okamžite odstráni používateľa a všetky položky. Tento krok nie je možné vrátiť späť a súbory nebude možné obnoviť.", + "forcing_refresh_library_files": "Vynútenie obnovy všetkých súborov knižnice", + "image_format": "Formát", "image_format_description": "WebP vytvára menšie súbory ako JPEG, ale kódovanie je pomalšie.", "image_prefer_embedded_preview": "Uprednostňovať vstavaný náhľad", "image_prefer_embedded_preview_setting_description": "Použiť vložené náhľady vo fotografiách RAW ako vstup pre spracovanie obrazu, ak sú k dispozícii. To môže vytvoriť presnejšie farby pre niektoré obrázky, ale kvalita náhľadu závisí od fotoaparátu a obrázok môže mať viac kompresných artefaktov.", - "image_prefer_wide_gamut": "Uprednostňovať široký rozsah", - "image_prefer_wide_gamut_setting_description": "", + "image_prefer_wide_gamut": "Uprednostňovať široký farebný rozsah", + "image_prefer_wide_gamut_setting_description": "Použiť Display P3 pre miniatúry. Toto lepšie zachováva živosť obrázkov so širokým farebným rozsahom. Obrázky sa môžu zobraziť odlišne na starších zariadeniach so starou verziou prehliadača. sRGB obrázky zostávajú sRGB, aby sa zabránilo farebným posunom.", + "image_preview_description": "Stredne veľký obrázok s odstránenými metadátami, používaný pri prezeraní jednej položky a na strojové učenie", "image_preview_format": "Formát ukážky", + "image_preview_quality_description": "Kvalita náhľadu v stupnici od 1 do 100. Vyššia hodnota znamená lepšiu kvalitu, ale produkuje väčšie súbory a môže znížiť odozvu aplikácie. Nastavenie nižšej hodnoty môže ovplyvniť kvalitu strojového učenia.", "image_preview_resolution": "Rozlíšenie náhľadu", "image_preview_resolution_description": "Používa sa pri prezeraní jednej fotografie a pre strojové učenie. Vyššie rozlíšenie zachová viac detailov, ale kódovanie trvá dlhšie, súbory sú väčšie, a môže znížiť rýchlosť aplikácie.", + "image_preview_title": "Nastavenia Náhľadov", "image_quality": "Kvalita", "image_quality_description": "", "image_resolution": "Rozlíšenie", - "image_settings": "", - "image_settings_description": "Nastavenie kvality a rozlíšenia generovaných obrázkov", + "image_resolution_description": "Vyššie rozlíšenie môže zachovať viac detailov, ale kódovanie trvá dlhšie, súbory sú väčšie a môže to znížiť rýchlosť odozvy aplikácie.", + "image_settings": "Nastavenia Obrázkov", + "image_settings_description": "Spravovať kvalitu a rozlíšenie generovaných obrázkov", + "image_thumbnail_description": "Malá miniatúra s odstránenými metadátami, používané pri zobrazovaní skupín fotiek ako na hlavnej časovej osi", "image_thumbnail_format": "Formát náhľadu", + "image_thumbnail_quality_description": "Kvalita miniatúry v stupnici od 1 do 100. Vyššia hodnota znamená lepšiu kvalitu, ale produkuje väčšie súbory a môže znížiť odozvu aplikácie.", "image_thumbnail_resolution": "", "image_thumbnail_resolution_description": "", - "job_settings": "", - "job_settings_description": "", + "image_thumbnail_title": "Nastavenia miniatúr", + "job_concurrency": "Súbežnosť úlohy - {job}", + "job_created": "Úloha bola vytvorená", + "job_not_concurrency_safe": "Táto úloha nie je bezpečná pre súbežné spracovanie", + "job_settings": "Nastavenia Úloh", + "job_settings_description": "Spravovať súbežnosť úloh", + "job_status": "Stav Úloh", + "jobs_delayed": "{jobCount, plural, one {# oneskorený} few {# oneskorené} other {# oneskorených}}", + "jobs_failed": "{jobCount, plural, one {# neúspešný} few {# neúspešné} other {# neúspešných}}", "library_created": "Vytvorená knižnica: {library}", - "library_cron_expression": "Cron výraz", + "library_cron_expression": "Výraz pre Cron", "library_cron_expression_description": "Nastaviť skenovací interval pomocou formátu cron. Viac informácií nájdete napr. na Crontab Guru", - "library_cron_expression_presets": "", - "library_deleted": "Knižnica vymazaná", - "library_scanning": "Periodické skenovanie", + "library_cron_expression_presets": "Predvoľby výrazu pre Cron", + "library_deleted": "Knižnica bola vymazaná", + "library_import_path_description": "Zvoľte priečinok na importovanie. Tento priečinok vrátane podpriečinkov bude skenovaný pre obrázky a videá.", + "library_scanning": "Pravidelné skenovanie", "library_scanning_description": "Nastaviť pravidelné skenovanie knižnice", "library_scanning_enable_description": "Zapnúť pravidelné skenovanie knižnice", "library_settings": "Externá knižnica", "library_settings_description": "Spravovať nastavenia externej knižnice", - "library_tasks_description": "", - "library_watching_enable_description": "Sledovať zmeny súborov v externých knižniciach", - "library_watching_settings": "", + "library_tasks_description": "Vykonať úlohy knižnice", + "library_watching_enable_description": "Sledovať externé knižnice pre zmeny v súboroch", + "library_watching_settings": "Sledovanie knižnice (EXPERIMENTÁLNE)", "library_watching_settings_description": "Automaticky sledovať zmenené súbory", - "logging_enable_description": "", - "logging_level_description": "", - "logging_settings": "", + "logging_enable_description": "Povoliť zaznamenávanie", + "logging_level_description": "Ak je povolené, akú úroveň zaznamenávania použiť.", + "logging_settings": "Zaznamenávanie", "machine_learning_clip_model": "Model CLIP", + "machine_learning_clip_model_description": "Názov modelu CLIP je uvedený tu. Pamätajte, že pri zmene modelu je nutné znovu spustiť úlohu 'Inteligentné vyhľadávanie' pre všetky obrázky.", "machine_learning_duplicate_detection": "Detekcia duplikátov", - "machine_learning_duplicate_detection_enabled": "Zapnúť detekciu duplikátov", - "machine_learning_duplicate_detection_enabled_description": "", - "machine_learning_duplicate_detection_setting_description": "", - "machine_learning_enabled": "Zapnúť strojové učenie", - "machine_learning_enabled_description": "", - "machine_learning_facial_recognition": "Rozpoznávanie tváre", - "machine_learning_facial_recognition_description": "", - "machine_learning_facial_recognition_model": "", - "machine_learning_facial_recognition_model_description": "", - "machine_learning_facial_recognition_setting_description": "", - "machine_learning_max_detection_distance": "", - "machine_learning_max_detection_distance_description": "", - "machine_learning_max_recognition_distance": "", - "machine_learning_max_recognition_distance_description": "", - "machine_learning_min_detection_score": "", - "machine_learning_min_detection_score_description": "", - "machine_learning_min_recognized_faces": "", - "machine_learning_min_recognized_faces_description": "", - "machine_learning_settings": "", - "machine_learning_settings_description": "", - "machine_learning_smart_search": "", + "machine_learning_duplicate_detection_enabled": "Povoliť detekciu duplikátov", + "machine_learning_duplicate_detection_enabled_description": "Ak je vypnuté, presne identické položky budú stále deduplikované.", + "machine_learning_duplicate_detection_setting_description": "Použiť CLIP embeddings na identifikáciu pravdepodobných duplikátov", + "machine_learning_enabled": "Povoliť strojové učenie", + "machine_learning_enabled_description": "Ak je vypnuté, všetky funkcie strojového učenia (ML) budú vypnuté, bez ohľadu na nastavenia nižšie.", + "machine_learning_facial_recognition": "Rozpoznávanie tvárí", + "machine_learning_facial_recognition_description": "Detekovať, rozpoznať a zoskupiť tváre na obrázkoch", + "machine_learning_facial_recognition_model": "Model pre rozpoznávanie tvárí", + "machine_learning_facial_recognition_model_description": "Modely sú zoradené od najväčšieho po najmenší. Väčšie modely sú pomalšie a vyžadujú viac pamäte, ale poskytujú lepšie výsledky. Pamätajte, že po zmene modelu je potrebné znovu spustiť úlohu detekcie tvárí pre všetky obrázky.", + "machine_learning_facial_recognition_setting": "Povoliť rozpoznávanie tvárí", + "machine_learning_facial_recognition_setting_description": "Ak je vypnuté, obrázky nebudú spracované pre rozpoznávanie tvárí a nebudú sa zobrazovať v sekcii Ľudia na stránke Preskúmať.", + "machine_learning_max_detection_distance": "Maximálna detekčná odchylka", + "machine_learning_max_detection_distance_description": "Maximálna odchylka medzi dvoma obrázkami, aby boli považované za duplikáty, v rozsahu od 0.001 do 0.1. Vyššie hodnoty odhalia viac duplikátov, ale môžu viesť k falošným pozitívam.", + "machine_learning_max_recognition_distance": "Maximálna rozpoznávacia odchylka", + "machine_learning_max_recognition_distance_description": "Maximálna odchylka medzi dvoma tvárami, aby boli považované za rovnakú osobu, v rozsahu od 0 do 2. Zníženie tejto hodnoty môže zabrániť označeniu dvoch ľudí za tú istú osobu, zatiaľ čo zvýšenie môže zabrániť označeniu jednej osoby za dve rôzne osoby. Pamätajte, že je jednoduchšie spojiť dvoch ľudí ako rozdeliť jednu osobu na dve, takže je lepšie voliť nižší prah, ak je to možné.", + "machine_learning_min_detection_score": "Minimálne detekčné skóre", + "machine_learning_min_detection_score_description": "Minimálne skóre dôveryhodnosti pre detekciu tváre v rozsahu od 0 do 1. Nižšie hodnoty odhalia viac tvárí, ale môžu viesť k falošným pozitivním výsledkom.", + "machine_learning_min_recognized_faces": "Minimum rozpoznaných tvárí", + "machine_learning_min_recognized_faces_description": "Minimálny počet rozpoznaných tvárí potrebných na vytvorenie osoby. Zvýšením tejto hodnoty sa zvyšuje presnosť rozpoznávania tvárí, ale tiež sa zvyšuje pravdepodobnosť, že tvár nebude priradená osobe.", + "machine_learning_settings": "Nastavenia strojového učenia", + "machine_learning_settings_description": "Spravovať funkcie a nastavenia strojového učenia", + "machine_learning_smart_search": "Inteligentné vyhľadávanie", "machine_learning_smart_search_description": "", + "machine_learning_smart_search_enabled": "Povoliť inteligentné vyhľadávanie", "machine_learning_smart_search_enabled_description": "", - "machine_learning_url_description": "", - "manage_log_settings": "", - "map_dark_style": "", - "map_enable_description": "", - "map_light_style": "", + "machine_learning_url_description": "URL adresa servera pre strojové učenie", + "manage_log_settings": "Spravovať nastavenia logovania", + "map_dark_style": "Tmavý štýl", + "map_enable_description": "Povoliť funkcie mapy", + "map_gps_settings": "Nastavenia Mapy & GPS", + "map_light_style": "Svetlý štýl", "map_reverse_geocoding": "", - "map_reverse_geocoding_enable_description": "", + "map_reverse_geocoding_enable_description": "Povoliť reverzné geokódovanie", "map_reverse_geocoding_settings": "", "map_settings": "Mapa", - "map_settings_description": "", + "map_settings_description": "Spravovať nastavenia mapy", "map_style_description": "", + "metadata_extraction_job": "Extrahovať metadáta", "metadata_extraction_job_description": "", + "metadata_faces_import_setting": "Povoliť import tváre", + "metadata_settings": "Nastavenia metadát", + "metadata_settings_description": "Spravovať nastavenia metadát", + "migration_job": "Migrácia", "migration_job_description": "", - "notification_email_from_address": "", + "notification_email_from_address": "Z adresy", "notification_email_from_address_description": "", "notification_email_host_description": "", - "notification_email_ignore_certificate_errors": "", + "notification_email_ignore_certificate_errors": "Ignorovať chyby certifikátu", "notification_email_ignore_certificate_errors_description": "", "notification_email_password_description": "", - "notification_email_port_description": "", - "notification_email_sent_test_email_button": "", - "notification_email_setting_description": "", - "notification_email_test_email_failed": "", - "notification_email_test_email_sent": "", - "notification_email_username_description": "", - "notification_enable_email_notifications": "", - "notification_settings": "", - "notification_settings_description": "", + "notification_email_port_description": "Porty e-mailového servera (napr. 25, 465, alebo 587)", + "notification_email_sent_test_email_button": "Odoslať testovací e-mail a uložiť", + "notification_email_setting_description": "Nastavenie pre odosielanie e-mailových upozornení", + "notification_email_test_email": "Odoslať testovací email", + "notification_email_test_email_failed": "Odosielanie testovacieho e-mailu zlyhalo, skontrolujte hodnoty", + "notification_email_test_email_sent": "Testovací e-mail bol odoslaný na adresu {email}. Prosím skontrolujte si Doručenú poštu.", + "notification_email_username_description": "Používateľské meno, ktoré sa má použiť pri overovaní s e-mailovým serverom", + "notification_enable_email_notifications": "Povoliť e-mailové upozornenia", + "notification_settings": "Nastavenia upozornení", + "notification_settings_description": "Spravovať nastavenia upozornení, vrátane emailu", "oauth_auto_launch": "", "oauth_auto_launch_description": "", "oauth_auto_register": "", "oauth_auto_register_description": "", "oauth_button_text": "", - "oauth_client_id": "", - "oauth_client_secret": "", - "oauth_enable_description": "", + "oauth_client_id": "Client ID", + "oauth_client_secret": "Client Secret", + "oauth_enable_description": "Prihlásiť sa pomocou OAuth", "oauth_issuer_url": "", "oauth_mobile_redirect_uri": "", "oauth_mobile_redirect_uri_override": "", "oauth_mobile_redirect_uri_override_description": "", "oauth_scope": "", - "oauth_settings": "", - "oauth_settings_description": "", + "oauth_settings": "OAuth", + "oauth_settings_description": "Spravovať nastavenia prihlásenia OAuth", "oauth_signing_algorithm": "", "oauth_storage_label_claim": "", "oauth_storage_label_claim_description": "", @@ -156,15 +190,23 @@ "oauth_storage_quota_claim_description": "", "oauth_storage_quota_default": "", "oauth_storage_quota_default_description": "", - "password_enable_description": "", - "password_settings": "", - "password_settings_description": "", - "server_external_domain_settings": "", + "password_enable_description": "Prihlásiť sa pomocou emailu a hesla", + "password_settings": "Prihlásenie cez heslo", + "password_settings_description": "Spravovať nastavenia prihlásenia cez heslo", + "refreshing_all_libraries": "Obnovujú sa všetky knižnice", + "registration": "Registrácia administrátora", + "repair_all": "Opraviť Všetko", + "require_password_change_on_login": "Vyžadovať od používateľa zmenu hesla pri prvom prihlásení", + "reset_settings_to_default": "Obnoviť pôvodné nastavenia", + "scanning_library": "Knižnica sa skenuje", + "search_jobs": "Vyhľadať úlohy...", + "send_welcome_email": "Odoslať uvítací e-mail", + "server_external_domain_settings": "Externá doména", "server_external_domain_settings_description": "", - "server_settings": "", - "server_settings_description": "", - "server_welcome_message": "", - "server_welcome_message_description": "", + "server_settings": "Nastavenia servera", + "server_settings_description": "Spravovať nastavenia servera", + "server_welcome_message": "Uvítacia správa", + "server_welcome_message_description": "Správa, ktorá sa zobrazí na prihlasovacej stránke.", "sidecar_job_description": "", "slideshow_duration_description": "", "smart_search_job_description": "", @@ -174,23 +216,25 @@ "storage_template_migration_job": "", "storage_template_settings": "", "storage_template_settings_description": "", - "theme_custom_css_settings": "", + "system_settings": "Nastavenia systému", + "theme_custom_css_settings": "Vlastné CSS", "theme_custom_css_settings_description": "", - "theme_settings": "", - "theme_settings_description": "", + "theme_settings": "Nastavenia témovania", + "theme_settings_description": "Spravovať prispôsobenie webového rozhrania Immich", + "thumbnail_generation_job": "Generovať Miniatúry", "thumbnail_generation_job_description": "", "transcode_policy_description": "", "transcoding_acceleration_api": "", "transcoding_acceleration_api_description": "", - "transcoding_acceleration_nvenc": "", - "transcoding_acceleration_qsv": "", + "transcoding_acceleration_nvenc": "NVENC (vyžaduje grafickú kartu NVIDIA)", + "transcoding_acceleration_qsv": "Quick Sync (vyžaduje 7. generáciu Intel procesora alebo novšie)", "transcoding_acceleration_rkmpp": "", - "transcoding_acceleration_vaapi": "", + "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "", "transcoding_accepted_audio_codecs_description": "", "transcoding_accepted_video_codecs": "", "transcoding_accepted_video_codecs_description": "", - "transcoding_advanced_options_description": "", + "transcoding_advanced_options_description": "Možnosti, ktoré by väčšina používateľov nemala meniť", "transcoding_audio_codec": "", "transcoding_audio_codec_description": "", "transcoding_bitrate_description": "", @@ -199,7 +243,7 @@ "transcoding_constant_rate_factor": "", "transcoding_constant_rate_factor_description": "", "transcoding_disabled_description": "", - "transcoding_hardware_acceleration": "", + "transcoding_hardware_acceleration": "Hardvérová akcelerácia", "transcoding_hardware_acceleration_description": "", "transcoding_hardware_decoding": "", "transcoding_hardware_decoding_setting_description": "", @@ -218,13 +262,13 @@ "transcoding_reference_frames": "", "transcoding_reference_frames_description": "", "transcoding_required_description": "", - "transcoding_settings": "", + "transcoding_settings": "Nastavenia video transkódovania", "transcoding_settings_description": "", "transcoding_target_resolution": "", "transcoding_target_resolution_description": "", "transcoding_temporal_aq": "", "transcoding_temporal_aq_description": "", - "transcoding_threads": "", + "transcoding_threads": "Vlákna", "transcoding_threads_description": "", "transcoding_tone_mapping": "", "transcoding_tone_mapping_description": "", @@ -235,99 +279,125 @@ "transcoding_two_pass_encoding_setting_description": "", "transcoding_video_codec": "", "transcoding_video_codec_description": "", - "trash_enabled_description": "", - "trash_number_of_days": "", + "trash_enabled_description": "Povoliť funkcie koša", + "trash_number_of_days": "Počet dní", "trash_number_of_days_description": "", - "trash_settings": "", - "trash_settings_description": "", - "user_delete_delay_settings": "", + "trash_settings": "Nastavenia koša", + "trash_settings_description": "Spravovať nastavenia koša", + "user_delete_delay_settings": "Odstrániť oneskorenie", "user_delete_delay_settings_description": "", - "user_settings": "Používateľské nastavenia", - "user_settings_description": "", - "version_check_enabled_description": "", - "version_check_settings": "Pozrieť verziu", - "version_check_settings_description": "", + "user_management": "Správa používateľov", + "user_password_has_been_reset": "Heslo používateľa bolo resetované:", + "user_settings": "Nastavenia používateľa", + "user_settings_description": "Spravovať používateľské nastavenia", + "user_successfully_removed": "Používateľ {email} bol úspešne odstránený.", + "version_check_enabled_description": "Povoliť kontrolu verzie", + "version_check_settings": "Kontrola verzie", + "version_check_settings_description": "Povoliť/zakázať upozornenia na novú verziu", + "video_conversion_job": "Prekódovať videá", "video_conversion_job_description": "" }, - "admin_email": "", - "admin_password": "", + "admin_email": "Administrátorský email", + "admin_password": "Administrátorské heslo", "administration": "Administrácia", "advanced": "Pokročilé", - "album_added": "Album pridaný", - "album_added_notification_setting_description": "", + "album_added": "Album bol pridaný", + "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď ste pridaní do zdieľaného albumu", "album_cover_updated": "", + "album_delete_confirmation": "Ste si istý, že chcete odstrániť album {album}?", "album_info_updated": "Informácie albumu aktualizované", "album_leave": "Opustiť album?", - "album_leave_confirmation": "Naozaj chete opustiť album {album}?", - "album_name": "Meno albumu", + "album_leave_confirmation": "Ste si istý, že chcete opustiť album {album}?", + "album_name": "Názov albumu", "album_options": "Nastavenia albumu", "album_remove_user": "Odstrániť používateľa?", - "album_remove_user_confirmation": "Naozaj chete odstrániť používateľa {user}?", - "album_updated": "", - "album_updated_setting_description": "", + "album_remove_user_confirmation": "Ste si istý, že chcete odstrániť používateľa {user}?", + "album_updated": "Album bol aktualizovaný", + "album_updated_setting_description": "Obdržať e-mailové upozornenie, keď v zdieľanom albume pribudnú nové položky", + "album_user_left": "Opustil {album}", + "album_with_link_access": "Umožnite komukoľvek s odkazom pozrieť si fotky a ľudí v tomto albume.", "albums": "Albumy", "all": "Všetko", - "all_people": "", - "allow_dark_mode": "", - "allow_edits": "", - "api_key": "", - "api_keys": "", - "app_settings": "", + "all_albums": "Všetky albumy", + "all_people": "Všetci ľudia", + "all_videos": "Všetky videa", + "allow_dark_mode": "Povoliť tmavý režim", + "allow_edits": "Povoliť úpravy", + "anti_clockwise": "Proti smeru hodinových ručičiek", + "api_key": "API Klúč", + "api_key_empty": "Názov vášho API kĺuča by nemal byť prázdny", + "api_keys": "API Kľúče", + "app_settings": "Nastavenia Aplikácie", "appears_in": "", "archive": "Archivovať", "archive_or_unarchive_photo": "", "archived": "", + "are_you_sure_to_do_this": "Ste si istý, že to chcete urobiť?", + "asset_added_to_album": "Pridané do albumu", + "asset_adding_to_album": "Pridáva sa do albumu...", "asset_offline": "", - "assets": "položiek", - "authorized_devices": "", + "asset_skipped": "Preskočené", + "asset_skipped_in_trash": "V koši", + "asset_uploaded": "Nahrané", + "asset_uploading": "Nahráva sa...", + "assets": "Položky", + "authorized_devices": "Autorizované zariadenia", "back": "Späť", "backward": "", + "birthdate_saved": "Dátum narodenia bol úspešne uložený", "blurred_background": "", - "camera": "", - "camera_brand": "", - "camera_model": "", + "buy": "Kúpiť Immich", + "camera": "Fotoaparát", + "camera_brand": "Výrobca fotoaparátu", + "camera_model": "Model fotoaparátu", "cancel": "Zrušiť", - "cancel_search": "", + "cancel_search": "Zrušiť vyhľadávanie", "cannot_merge_people": "", - "cannot_update_the_description": "", + "cannot_update_the_description": "Popis nie je možné aktualizovať", "cant_apply_changes": "", "cant_get_faces": "", "cant_search_people": "", "cant_search_places": "", - "change_date": "", + "change_date": "Upraviť dátum", "change_expiration_time": "Zmeniť čas vypršania", - "change_location": "", - "change_name": "", + "change_location": "Upraviť lokáciu", + "change_name": "Upraviť meno", "change_name_successfully": "", - "change_password": "Zmeniť heslo", + "change_password": "Zmeniť Heslo", "change_your_password": "", "changed_visibility_successfully": "", - "check_logs": "", - "city": "", + "check_all": "Skontrolovať Všetko", + "check_logs": "Skontrolovať logy", + "city": "Mesto", "clear": "VYMAZAŤ", - "clear_all": "", - "clear_message": "", - "clear_value": "", - "close": "", + "clear_all": "Vymazať všetko", + "clear_all_recent_searches": "Vymazať nedávne vyhľadávania", + "clear_message": "Vymazať správu", + "clear_value": "Vymazať hodnotu", + "clockwise": "V smere hodinových ručičiek", + "close": "Zatvoriť", "collapse_all": "", "color_theme": "", - "comment_options": "", - "comments_are_disabled": "", - "confirm": "", - "confirm_admin_password": "", - "confirm_password": "Potvrďte heslo", + "comment_deleted": "Komentár bol odstránený", + "comment_options": "Možnosti komentára", + "comments_are_disabled": "Komentáre sú vypnuté", + "confirm": "Potvrdiť", + "confirm_admin_password": "Potvrdiť Administrátorské Heslo", + "confirm_delete_shared_link": "Ste si istý, že chcete odstrániť tento zdieľaný odkaz?", + "confirm_password": "Potvrďiť heslo", "contain": "", - "context": "", - "continue": "", - "copied_image_to_clipboard": "", - "copy_error": "", + "context": "Kontext", + "continue": "Pokračovať", + "copied_image_to_clipboard": "Obrázok skopírovaný do schránky.", + "copied_to_clipboard": "Skopírované do schránky!", + "copy_error": "Chyba pri kopírovaní", "copy_file_path": "", - "copy_image": "", - "copy_link": "", - "copy_link_to_clipboard": "", - "copy_password": "", - "copy_to_clipboard": "", - "country": "", + "copy_image": "Skopírovať obrázok", + "copy_link": "Skopírovať odkaz", + "copy_link_to_clipboard": "Skopírovať do schránky", + "copy_password": "Skopírovať heslo", + "copy_to_clipboard": "Skopírovať do schránky", + "country": "Štát", "cover": "", "covers": "", "create": "Vytvoriť", @@ -335,10 +405,11 @@ "create_library": "Vytvoriť knižnicu", "create_link": "Vytvoriť odkaz", "create_link_to_share": "Vytvoriť odkaz na zdieľanie", - "create_new_person": "", + "create_new_person": "Vytvoriť novú osobu", "create_new_user": "Vytvorenie nového používateľa", + "create_tag": "Vytvoriť značku", "create_user": "Vytvoriť používateľa", - "created": "", + "created": "Vytvorené", "current_device": "Aktuálne zariadenie", "custom_locale": "", "custom_locale_description": "", @@ -364,14 +435,15 @@ "description": "Popis", "details": "PODROBNOSTI", "direction": "Smer", + "disabled": "Vypnuté", "disallow_edits": "", "discord": "Discord", - "discover": "", + "discover": "Preskúmať", "dismiss_all_errors": "", "dismiss_error": "", "display_options": "Zobraziť možnosti", "display_order": "", - "display_original_photos": "", + "display_original_photos": "Zobraziť pôvodné fotografie", "display_original_photos_setting_description": "", "do_not_show_again": "Túto správu znova nezobrazovať", "documentation": "Dokumentácia", @@ -395,7 +467,7 @@ "edit_date": "Upraviť dátum", "edit_date_and_time": "Upraviť dátum a čas", "edit_exclusion_pattern": "", - "edit_faces": "", + "edit_faces": "Upraviť tváre", "edit_import_path": "", "edit_import_paths": "", "edit_key": "Upraviť kľúč", @@ -409,6 +481,7 @@ "edited": "Upravené", "editor": "", "editor_close_without_save_prompt": "Úpravy nebudú uložené", + "editor_crop_tool_h2_aspect_ratios": "Pomer strán", "editor_crop_tool_h2_rotation": "Rotácia", "email": "E-mail", "empty": "", @@ -418,7 +491,7 @@ "enabled": "Aktivovaný", "end_date": "", "error": "Chyba", - "error_loading_image": "", + "error_loading_image": "Chyba pri načítaní obrázku", "error_title": "Chyba - niečo sa pokazilo", "errors": { "unable_to_add_album_users": "", @@ -558,7 +631,7 @@ "loading_search_results_failed": "", "log_out": "Odhlásiť sa", "log_out_all_devices": "", - "login_has_been_disabled": "", + "login_has_been_disabled": "Prihlásenie bolo vypnuté.", "look": "", "loop_videos": "", "loop_videos_description": "", @@ -570,7 +643,7 @@ "manage_your_api_keys": "", "manage_your_devices": "", "manage_your_oauth_connection": "", - "map": "", + "map": "Mapa", "map_marker_with_image": "", "map_settings": "Nastavenia máp", "media_type": "", @@ -595,12 +668,13 @@ "new_password": "Nové heslo", "new_person": "", "new_user_created": "", + "new_version_available": "JE DOSTUPNÁ NOVÁ VERZIA", "newest_first": "", "next": "Ďalej", "next_memory": "", "no": "", "no_albums_message": "", - "no_archived_assets_message": "", + "no_archived_assets_message": "Archivovať fotografie a videá, aby sa skryli zo zobrazenia Fotografie", "no_assets_message": "", "no_exif_info_available": "", "no_explore_results_message": "", @@ -612,21 +686,23 @@ "no_shared_albums_message": "", "not_in_any_album": "", "notes": "", - "notification_toggle_setting_description": "", + "notification_toggle_setting_description": "Povoliť e-mailové upozornenia", "notifications": "Oznámenia", - "notifications_setting_description": "", - "oauth": "", + "notifications_setting_description": "Spravovať upozornenia", + "oauth": "OAuth", "offline": "", "ok": "", "oldest_first": "", + "onboarding_welcome_user": "Vitaj, {user}", "online": "", "only_favorites": "", "only_refreshes_modified_files": "", "open_the_search_filters": "", "options": "Nastavenia", - "organize_your_library": "", + "or": "alebo", + "organize_your_library": "Usporiadajte svoju knižnicu", "other": "", - "other_devices": "", + "other_devices": "Ďalšie zariadenia", "other_variables": "", "owned": "Vlastnené", "owner": "Vlastník", @@ -655,11 +731,12 @@ "permanently_delete": "", "permanently_deleted_asset": "", "photos": "Fotografie", + "photos_and_videos": "Fotografie & Videa", "photos_from_previous_years": "", "pick_a_location": "", - "place": "", + "place": "Miesto", "places": "Miesta", - "play": "", + "play": "Prehrať", "play_memories": "", "play_motion_photo": "", "play_or_pause_video": "", @@ -672,33 +749,42 @@ "previous_or_next_photo": "", "primary": "", "profile_picture_set": "", + "public_album": "Verejný album", "public_share": "", + "purchase_activated_time": "Aktivované {date, date}", + "purchase_button_activate": "Aktivovať", + "purchase_button_never_show_again": "Už viac nezobrazovať", + "purchase_panel_title": "Podporiť projekt", "range": "", "raw": "", "reaction_options": "", "read_changelog": "", - "recent": "", + "recent": "Nedávne", "recent_searches": "", - "refresh": "", - "refreshed": "", + "refresh": "Obnoviť", + "refresh_metadata": "Obnoviť metadáta", + "refresh_thumbnails": "Obnoviť miniatúry", + "refreshed": "Aktualizované", "refreshes_every_file": "", - "remove": "", + "remove": "Odstrániť", "remove_deleted_assets": "", "remove_from_album": "Odstrániť z albumu", "remove_from_favorites": "", "remove_from_shared_link": "", - "repair": "", + "remove_user": "Odstrániť používateľa", + "repair": "Opraviť", "repair_no_results_message": "", "replace_with_upload": "", - "require_password": "", - "reset": "", - "reset_password": "", + "require_password": "Vyžadovať heslo", + "reset": "Resetovať", + "reset_password": "Obnoviť heslo", "reset_people_visibility": "", "reset_settings_to_default": "", "restore": "Obnoviť", - "restore_user": "", + "restore_user": "Obnoviť používateľa", + "resume": "Pokračovať", "retry_upload": "", - "review_duplicates": "", + "review_duplicates": "Skontrolovať duplikáty", "role": "", "save": "Uložiť", "saved_profile": "", @@ -707,10 +793,11 @@ "scan_all_libraries": "", "scan_all_library_files": "", "scan_new_library_files": "", - "scan_settings": "", + "scan_settings": "Nastavenia skenovania", "search": "Vyhľadávanie", - "search_albums": "", + "search_albums": "Hľadať albumy", "search_by_context": "", + "search_by_filename_example": "napr. IMG_1234.JPG alebo PNG", "search_camera_make": "", "search_camera_model": "", "search_city": "", @@ -718,8 +805,9 @@ "search_for_existing_person": "", "search_people": "", "search_places": "", + "search_settings": "Hladať v nastaveniach", "search_state": "", - "search_timezone": "", + "search_timezone": "Vyhľadať časovú zónu...", "search_type": "", "search_your_photos": "Prehľadajte svoje obrázky", "searching_locales": "", @@ -729,48 +817,61 @@ "select_avatar_color": "", "select_face": "", "select_featured_photo": "", - "select_library_owner": "", + "select_library_owner": "Vybraťi vlastníka knižnice", "select_new_face": "", "select_photos": "Vybrať fotografie", - "selected": "", - "send_message": "", + "selected": "Vybraté", + "send_message": "Odoslať správu", + "send_welcome_email": "Odoslať uvítací e-mail", "server": "", - "server_stats": "", - "set": "", + "server_stats": "Štatistiky servera", + "server_version": "Verzia servera", + "set": "Nastaviť", "set_as_album_cover": "", - "set_as_profile_picture": "", - "set_date_of_birth": "", - "set_profile_picture": "", + "set_as_profile_picture": "Nastaviť ako profilový obrázok", + "set_date_of_birth": "Nastaviť dátum narodenia", + "set_profile_picture": "Nastaviť profilový obrázok", "set_slideshow_to_fullscreen": "", "settings": "Nastavenia", - "settings_saved": "", + "settings_saved": "Nastavenia boli uložené", "share": "Zdieľať", "shared": "Zdieľané", "shared_by": "", "shared_by_you": "", + "shared_from_partner": "Fotografie od {partner}", "shared_links": "Zdieľané odkazy", + "shared_with_partner": "Zďielané s {partner}", "sharing": "Zdieľanie", "sharing_sidebar_description": "", - "show_album_options": "", + "show_album_options": "Zobraziť možnosti albumu", + "show_albums": "Zobraziť albumy", "show_file_location": "", - "show_gallery": "", + "show_gallery": "Zobraziť galériu", "show_hidden_people": "", - "show_in_timeline": "", + "show_in_timeline": "Zobraziť na časovej osi", "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", + "show_keyboard_shortcuts": "Zobraziť klávesové skratky", "show_metadata": "Zobraziť metadáta", "show_or_hide_info": "", - "show_password": "", + "show_password": "Zobraziť heslo", "show_person_options": "", "show_progress_bar": "", - "show_search_options": "", + "show_search_options": "Zobraziť možnosti vyhľadávania", "shuffle": "", + "sign_out": "Odhlásiť sa", "sign_up": "", - "size": "", + "size": "Veľkosť", "skip_to_content": "", "slideshow": "", "slideshow_settings": "", - "sort_albums_by": "", + "sort_albums_by": "Zoradiť albumy podľa...", + "sort_created": "Dátum vytvorenia", + "sort_items": "Počet položiek", + "sort_modified": "Dátum úpravy", + "sort_oldest": "Najstaršia fotografia", + "sort_recent": "Najnovšia fotografia", + "sort_title": "Názov", + "source": "Zdroj", "stack": "Zoskupenie", "stack_selected_photos": "", "stacktrace": "", @@ -779,26 +880,30 @@ "status": "", "stop_motion_photo": "", "stop_photo_sharing": "Zastaviť zdieľanie vašich fotiek?", - "storage": "", + "storage": "Ukladací priestor", "storage_label": "", - "submit": "", + "submit": "Odoslať", "suggestions": "Návrhy", "sunrise_on_the_beach": "", "swap_merge_direction": "", "sync": "", + "tags": "Značky", "template": "", "theme": "Téma", "theme_selection": "", "theme_selection_description": "", "time_based_memories": "", "timezone": "Časové pásmo", + "to_archive": "Archivovať", + "to_change_password": "Zmeniť heslo", + "to_trash": "Kôš", "toggle_settings": "", "toggle_theme": "", "toggle_visibility": "", "total_usage": "", "trash": "Kôš", "trash_all": "", - "trash_no_results_message": "", + "trash_no_results_message": "Vymazané fotografie a videá sa zobrazia tu.", "type": "", "unarchive": "Odarchivovať", "unarchived": "", @@ -806,19 +911,22 @@ "unhide_person": "", "unknown": "", "unknown_album": "", - "unknown_year": "", + "unknown_year": "Neznámy rok", "unlink_oauth": "", "unlinked_oauth_account": "", + "unnamed_album_delete_confirmation": "Ste si istý, že chcete zmazať tento album?", + "unsaved_change": "Neuložená zmena", "unselect_all": "", "unstack": "Odskupiť", "up_next": "", "updated_password": "", "upload": "Nahrať", "upload_concurrency": "", + "upload_status_duplicates": "Duplikáty", "upload_status_errors": "Chyby", "upload_status_uploaded": "Nahrané", "upload_success": "Nahrávanie úspešné, pridané súbory sa zobrazia po obnovení stránky.", - "url": "", + "url": "Odkaz URL", "usage": "Použitie", "user": "Používateľ", "user_id": "Používateľské ID", @@ -835,16 +943,19 @@ "video": "Video", "video_hover_setting_description": "", "videos": "Videá", + "view": "Zobraziť", + "view_album": "Zobraziť Album", "view_all": "Zobraziť všetky", - "view_all_users": "", - "view_links": "Pozrieť linky", - "view_next_asset": "Pozrieť nasledujúci súbor", - "view_previous_asset": "Pozrieť predchádzajúci súbor", + "view_all_users": "Zobraziť všetkých používateľov", + "view_in_timeline": "Zobraziť v časovej osi", + "view_links": "Zobraziť odkazy", + "view_next_asset": "Zobraziť nasledujúci súbor", + "view_previous_asset": "Zobraziť predchádzajúci súbor", "viewer": "", "waiting": "", "warning": "Varovanie", "week": "Týždeň", - "welcome": "Vytajte", + "welcome": "Vitajte", "welcome_to_immich": "Vytajte v immich", "year": "Rok", "yes": "Áno", diff --git a/web/src/lib/i18n/sl.json b/i18n/sl.json similarity index 100% rename from web/src/lib/i18n/sl.json rename to i18n/sl.json diff --git a/web/src/lib/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json similarity index 99% rename from web/src/lib/i18n/sr_Cyrl.json rename to i18n/sr_Cyrl.json index 46e1dd2fe2..8ff3b5ed1b 100644 --- a/web/src/lib/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Хардверско убрзање", "transcoding_hardware_acceleration_description": "Екпериментално; много брже, али ће имати нижи квалитет при истој брзини преноса", "transcoding_hardware_decoding": "Хардверско декодирање", - "transcoding_hardware_decoding_setting_description": "Односи се само на НВЕНЦ, QSV и RKMPP. Омогућава убрзање од краја до краја уместо да само убрзава кодирање. Можда неће радити на свим видео снимцима.", + "transcoding_hardware_decoding_setting_description": "Омогућава убрзање од краја до краја уместо да само убрзава кодирање. Можда неће радити на свим видео снимцима.", "transcoding_hevc_codec": "ХЕВЦ кодек", "transcoding_max_b_frames": "Максимални Б-кадри", "transcoding_max_b_frames_description": "Више вредности побољшавају ефикасност компресије, али успоравају кодирање. Можда није компатибилно са хардверским убрзањем на старијим уређајима. 0 oneмогућава Б-кадре, док -1 аутоматски поставља ову вредност.", @@ -432,7 +432,7 @@ "birthdate_set_description": "Датум рођења се користи да би се израчунале године ове особе у добу одређене фотографије.", "blurred_background": "Замућена позадина", "bugs_and_feature_requests": "Грешке и захтеви за функције", - "build": "Сагради (Буилд)", + "build": "Build", "build_image": "Сагради (Буилд) имаге", "bulk_delete_duplicates_confirmation": "Да ли сте сигурни да желите групно да избришете {count, plural, one {# дуплиран елеменат} few {# дуплирана елемента} other {# дуплираних елемената}}? Ово ће задржати највеће средство сваке групе и трајно избрисати све друге дупликате. Не можете поништити ову радњу!", "bulk_keep_duplicates_confirmation": "Да ли сте сигурни да желите да задржите {count, plural, one {1 дуплирану датотеку} few {# дуплиране датотеке} other {# дуплираних датотека}}? Ово ће решити све дуплиране групе без брисања било чега.", @@ -888,6 +888,7 @@ "look": "Погледај", "loop_videos": "Понављајте видео записе", "loop_videos_description": "Омогућите за аутоматско понављање видео записа у прегледнику детаља.", + "main_branch_warning": "Употребљавате развојну верзију; строго препоручујемо употребу издате верзије!", "make": "Креирај", "manage_shared_links": "Управљајте дељеним везама", "manage_sharing_with_partners": "Управљајте дељењем са партнерима", diff --git a/web/src/lib/i18n/sr_Latn.json b/i18n/sr_Latn.json similarity index 99% rename from web/src/lib/i18n/sr_Latn.json rename to i18n/sr_Latn.json index be16584b37..dbe2f4f72b 100644 --- a/web/src/lib/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Hardversko ubrzanje", "transcoding_hardware_acceleration_description": "Ekperimentalno; mnogo brže, ali će imati niži kvalitet pri istoj brzini prenosa", "transcoding_hardware_decoding": "Hardversko dekodiranje", - "transcoding_hardware_decoding_setting_description": "Odnosi se samo na NVENC, QSV i RKMPP. Omogućava ubrzanje od kraja do kraja umesto da samo ubrzava kodiranje. Možda neće raditi na svim video snimcima.", + "transcoding_hardware_decoding_setting_description": "Omogućava ubrzanje od kraja do kraja umesto da samo ubrzava kodiranje. Možda neće raditi na svim video snimcima.", "transcoding_hevc_codec": "HEVC kodek", "transcoding_max_b_frames": "Maksimalni B-kadri", "transcoding_max_b_frames_description": "Više vrednosti poboljšavaju efikasnost kompresije, ali usporavaju kodiranje. Možda nije kompatibilno sa hardverskim ubrzanjem na starijim uređajima. 0 onemogućava B-kadre, dok -1 automatski postavlja ovu vrednost.", @@ -432,7 +432,7 @@ "birthdate_set_description": "Datum rođenja se koristi da bi se izračunale godine ove osobe u dobu određene fotografije.", "blurred_background": "Zamućena pozadina", "bugs_and_feature_requests": "Greške (bugs) i zahtevi za funkcije", - "build": "Sagradi (Build)", + "build": "Build", "build_image": "Sagradi (Build) image", "bulk_delete_duplicates_confirmation": "Da li ste sigurni da želite grupno da izbrišete {count, plural, one {# dupliran elemenat} few {# duplirana elementa} other {# dupliranih elemenata}}? Ovo će zadržati najveće sredstvo svake grupe i trajno izbrisati sve druge duplikate. Ne možete poništiti ovu radnju!", "bulk_keep_duplicates_confirmation": "Da li ste sigurni da želite da zadržite {count, plural, one {1 dupliranu datoteku} few {# duplirane datoteke} other {# dupliranih datoteka}}? Ovo će rešiti sve duplirane grupe bez brisanja bilo čega.", @@ -888,6 +888,7 @@ "look": "Pogledaj", "loop_videos": "Ponavljajte video zapise", "loop_videos_description": "Omogućite za automatsko ponavljanje video zapisa u pregledniku detalja.", + "main_branch_warning": "Upotrebljavate razvojnu verziju; strogo preporučujemo upotrebu izdate verzije!", "make": "Kreiraj", "manage_shared_links": "Upravljajte deljenim vezama", "manage_sharing_with_partners": "Upravljajte deljenjem sa partnerima", diff --git a/web/src/lib/i18n/sv.json b/i18n/sv.json similarity index 100% rename from web/src/lib/i18n/sv.json rename to i18n/sv.json diff --git a/web/src/lib/i18n/ta.json b/i18n/ta.json similarity index 99% rename from web/src/lib/i18n/ta.json rename to i18n/ta.json index 175dcd9bc9..e4201806f7 100644 --- a/web/src/lib/i18n/ta.json +++ b/i18n/ta.json @@ -41,6 +41,7 @@ "confirm_email_below": "உறுதிப்படுத்த, கீழே \"{email}\" என தட்டச்சு செய்யவும்", "confirm_reprocess_all_faces": "எல்லா முகங்களையும் மீண்டும் செயலாக்க விரும்புகிறீர்களா? இது பெயரிடப்பட்ட நபர்களையும் அழிக்கும்.", "confirm_user_password_reset": "{user} இன் கடவுச்சொல்லை நிச்சயமாக மீட்டமைக்க விரும்புகிறீர்களா?", + "create_job": "வேலையை உருவாக்கு", "disable_login": "உள்நுழைவை முடக்கு", "duplicate_detection_job_description": "ஒத்த படங்களைக் கண்டறிய, சொத்துக்களில் இயந்திரக் கற்றலை இயக்கவும். ஸ்மார்ட் தேடலை நம்பியுள்ளது", "exclusion_pattern_description": "உங்கள் நூலகத்தை ஸ்கேன் செய்யும் போது கோப்புகளையும் கோப்புறைகளையும் புறக்கணிக்க விலக்கு வடிவங்கள் உங்களை அனுமதிக்கின்றன. RAW கோப்புகள் போன்ற நீங்கள் இறக்குமதி செய்ய விரும்பாத கோப்புகளைக் கொண்ட கோப்புறைகள் உங்களிடம் இருந்தால் இது பயனுள்ளதாக இருக்கும்.", @@ -55,7 +56,7 @@ "image_format_description": "WebP, JPEG ஐ விட சிறிய கோப்புகளை உருவாக்குகிறது, ஆனால் குறியாக்கம் செய்ய மெதுவாக உள்ளது.", "image_prefer_embedded_preview": "உட்பொதிந்த படத்தை முன்னிடு", "image_prefer_embedded_preview_setting_description": "", - "image_prefer_wide_gamut": "", + "image_prefer_wide_gamut": "அகன்ற வண்ணவரம்பு தேர்வு", "image_prefer_wide_gamut_setting_description": "", "image_preview_format": "", "image_preview_resolution": "", diff --git a/web/src/lib/i18n/te.json b/i18n/te.json similarity index 100% rename from web/src/lib/i18n/te.json rename to i18n/te.json diff --git a/web/src/lib/i18n/th.json b/i18n/th.json similarity index 99% rename from web/src/lib/i18n/th.json rename to i18n/th.json index 83675d5040..6665c6ec83 100644 --- a/web/src/lib/i18n/th.json +++ b/i18n/th.json @@ -28,6 +28,7 @@ "added_to_favorites_count": "{count, number} รูปถูกเพิ่มเข้ารายการโปรด", "admin": { "add_exclusion_pattern_description": "เพิ่มรูปแบบการยกเว้น การ Glob โดยใช้ *, ** และ ? ถูกรองรับ ถ้าต้องการละเว้นไฟล์ทั้งหมดในไดเร็กทอรีใดๆที่ชื่อว่า \"Raw\" ให้ใช้ \"**/Raw/**\" ถ้าต้องการละเว้นไฟล์ทั้งหมดที่ลงท้ายด้วย \".tif\" ให้ใช้ \"**/*.tif\" ถ้าต้องการละเว้นพาธที่เริ่มจากไดเรกทอรีบนสุดให้ใช้ \"/พาธ/ที่ต้องการ/ละเว้น/**\"", + "asset_offline_description": "Immich", "authentication_settings": "ตั้งค่าการเข้าถึง", "authentication_settings_description": "จัดการรหัสผ่าน, OAuth, และตั้งค่าการเข้าถึงอื่นๆ", "authentication_settings_disable_all": "คุณแน่ใจว่าต้องการปิดวิธีการล็อกอินทั้งหมดหรือไม่? ล็อกอินจะถูกปิดทั้งหมด", @@ -54,6 +55,7 @@ "failed_job_command": "คำสั่ง {command} ของงาน {job} ล้มเหลว", "force_delete_user_warning": "คําเตือน: ขั้นตอนนี้จะลบผู้ใช้และสื่อทั้งหมดทันที ขั้นตอนนี้จะย้อนกลับมาไม่ได้และกู้คืนไฟล์ไม่ได้.", "forcing_refresh_library_files": "บังคับรีเฟรชไฟล์ทั้งหมด", + "image_format": "Format", "image_format_description": "WebP จะสร้างไฟล์ที่เล็กกว่า JPEG แต่ใช้เวลา encode นานกว่า", "image_prefer_embedded_preview": "ใช้พรีวิวแบบฝังตัว", "image_prefer_embedded_preview_setting_description": "ใช้พรีวิวฝังตัวในรูปภาพ RAW ในการวิเคราะห์รูปภาพถ้ามี แต่คุณภาพรูปภาพขึ้นอยู่กับกล้อง และอาจจะมีสิ่งตกค้างจากการย่อขนาดไฟล์", diff --git a/web/src/lib/i18n/tr.json b/i18n/tr.json similarity index 90% rename from web/src/lib/i18n/tr.json rename to i18n/tr.json index 0d5e1b88ce..b2f0559ce8 100644 --- a/web/src/lib/i18n/tr.json +++ b/i18n/tr.json @@ -151,7 +151,7 @@ "map_style_description": "style.json Harita ayarlarının URL'si", "metadata_extraction_job": "Meta verilerinden Ayıkla", "metadata_extraction_job_description": "GPS ve çözünürlük gibi ger bir varlığın meta veri bilgilerini ayıklayın", - "metadata_faces_import_setting": "Yüzleri alma aktif", + "metadata_faces_import_setting": "Yüz içe aktarmayı etkinleştir", "metadata_faces_import_setting_description": "Yüzleri, EXIF verileri ve sidecar dosyalardan getir", "metadata_settings": "Metaveri Ayarları", "metadata_settings_description": "Metaveri ayarlarını yönet", @@ -159,7 +159,7 @@ "migration_job_description": "Varlıklar ve yüzler için resim çerçeve önizlemelerini en yeni klasör yapısına aktar", "no_paths_added": "Yol eklenmedi", "no_pattern_added": "Desen eklenmedi", - "note_apply_storage_label_previous_assets": "Not: Depolama adresini daha önce yüklenmiş dosyalara uygulamak için", + "note_apply_storage_label_previous_assets": "Not: Daha önce yüklenen varlıklara Depolama Etiketi uygulamak için şu komutu çalıştırın", "note_cannot_be_changed_later": "NOT: Bu daha sonra değiştirilemez!", "note_unlimited_quota": "NOT: Sınırsız kota için 0 yazın", "notification_email_from_address": "Şu adresten", @@ -285,7 +285,7 @@ "transcoding_hardware_acceleration": "Donanım Hızlandırma", "transcoding_hardware_acceleration_description": "Deneysel; daha hızlı, fakat aynı bitrate ayarlarında daha düşük kaliteye sahip", "transcoding_hardware_decoding": "Donanım çözücü", - "transcoding_hardware_decoding_setting_description": "Sadece NVENC, QSV ve RKMPP için geçerli. Sadece işlemeyi hızlandırmak yerine uçtan uca hızlandırmayı etkinleştirir. Tüm videolarda çalışmayabilir.", + "transcoding_hardware_decoding_setting_description": "Uçtan uca hızlandırmayı, sadece kodlamayı hızlandırmanın yerine etkinleştirir. Tüm videolarda çalışmayabilir.", "transcoding_hevc_codec": "HEVC kodek", "transcoding_max_b_frames": "Maksimum B-kareler", "transcoding_max_b_frames_description": "Daha yüksek değerler sıkıştırma verimliliğini artırır, ancak kodlamayı yavaşlatır. Eski cihazlarda donanım hızlandırma ile uyumlu olmayabilir. 0, B-çerçevelerini devre dışı bırakır, -1 ise bu değeri otomatik olarak ayarlar.", @@ -313,7 +313,7 @@ "transcoding_tone_mapping_description": "HDR videoların SDR'ye dönüştürülürken görünümünü korumayı amaçlar. Her algoritma renk, detay ve parlaklık için farklı dengeleme yapar. Hable detayları korur, Mobius renkleri korur ve Reinhard parlaklığı korur.", "transcoding_tone_mapping_npl": "Ton eşleme NPL", "transcoding_tone_mapping_npl_description": "Renkler, bu parlaklıkta bir ekran için normal görünecek şekilde ayarlanacaktır. Karşıt olarak, daha düşük değerler videonun parlaklığını artırır ve tersi de geçerlidir çünkü ekranın parlaklığını telafi eder. 0 bu değeri otomatik olarak ayarlar.", - "transcoding_transcode_policy": "Dönüştürme(çevirme) politikası", + "transcoding_transcode_policy": "Dönüştürme (çevirme) politikası", "transcoding_transcode_policy_description": "Bir videonun ne zaman kod dönüştürülmesi gerektiğine ilişkin ilke. Dönüştürme devre dışı bırakılmadığı sürece HDR videolar her zaman dönüştürülür.", "transcoding_two_pass_encoding": "İki geçişli kodlama", "transcoding_two_pass_encoding_setting_description": "Daha iyi kodlanmış videolar üretmek için iki geçişte kod dönüştürün. Maksimum bit hızı etkinleştirildiğinde (H.264 ve HEVC ile çalışması için gereklidir), bu mod maksimum bit hızına dayalı bir bit hızı aralığı kullanır ve CRF'yi yok sayar. VP9 için, maksimum bit hızı devre dışı bırakılırsa CRF kullanılabilir.", @@ -330,13 +330,13 @@ "user_delete_delay": "{user} hesabı ve varlıkları {delay, plural, one {# day} other {# days}} gün içinde kalıcı olarak silinmek için planlandı.", "user_delete_delay_settings": "Silme gecikmesi", "user_delete_delay_settings_description": "Bir kullanıcının hesabını ve varlıklarını kalıcı olarak silmek için kaldırıldıktan sonra gereken gün sayısı. Kullanıcı silme işi, silinmeye hazır kullanıcıları kontrol etmek için gece yarısı çalışır. Bu ayardaki değişiklikler bir sonraki yürütmede değerlendirilecektir.", - "user_delete_immediately": "{Kullanıcı}'nın hesabı ve varlıkları hemen kalıcı olarak silinmek üzere sıraya alınacak.", - "user_delete_immediately_checkbox": "Kullanıcıyı ve tüm varlıklarını kalıcı olarak silmek için sıraya koy", + "user_delete_immediately": "{user}'in hesabı ve varlıkları hemen kalıcı olarak silinmek üzere sıraya alınacak.", + "user_delete_immediately_checkbox": "Kullanıcı ve varlıkları hemen silinmek üzere sıraya al", "user_management": "Kullanıcı Yönetimi", "user_password_has_been_reset": "Kullanıcının şifresi sıfırlandı:", "user_password_reset_description": "Lütfen kullanıcıya geçici şifreyi sağlayın ve bir sonraki oturum açışında şifreyi değiştirmesi gerektiğini bildirin.", "user_restore_description": "{user} kullanıcısı geri yüklenecek.", - "user_restore_scheduled_removal": "Kullanıcıyı geri yükle - {tarih, tarih, uzun} tarihinde zamanlanmış kaldırma", + "user_restore_scheduled_removal": "Kullanıcıyı geri yükle - {date, date, long} tarihinde planlanan kaldırma", "user_settings": "Kullanıcı Ayarları", "user_settings_description": "Kullanıcı Ayarlarını Yönet", "user_successfully_removed": "Kullanıcı {email} başarıyla kaldırıldı.", @@ -388,12 +388,13 @@ "api_key_empty": "Apı Anahtarı isminiz boş olmamalı", "api_keys": "API Anahtarları", "app_settings": "Uygulama Ayarları", - "appears_in": "", + "appears_in": "Şurada görünür", "archive": "Arşiv", "archive_or_unarchive_photo": "Fotoğrafı arşivle/arşivden çıkar", "archive_size": "Arşiv boyutu", "archive_size_description": "İndirmeler için arşiv boyutunu yapılandırın (GiB cinsinden)", "archived": "", + "archived_count": "{count, plural, other {# arşivlendi}}", "are_these_the_same_person": "Bunlar aynı kişi mi?", "are_you_sure_to_do_this": "Bunu yapmak istediğinize emin misiniz?", "asset_added_to_album": "Albüme eklendi", @@ -401,26 +402,38 @@ "asset_description_updated": "Varlık açıklaması güncellendi", "asset_filename_is_offline": "Varlık {filename} çevrimdışı", "asset_has_unassigned_faces": "Varlık, atanmamış yüzler içeriyor", - "asset_offline": "Varlık çevrimdışı", - "asset_offline_description": "Bu varlık çevrimdışı. Immich dosya konumuna erişemiyor. Lütfen varlığın kullanılabilir olduğundan emin olun ve ardından kitaplığı yeniden tarayın.", + "asset_hashing": "Karma (hashleme) oluşturuluyor...", + "asset_offline": "Varlık Çevrim Dışı", + "asset_offline_description": "Bu harici varlık artık diskte bulunmuyor. Yardım için lütfen Immich yöneticinizle iletişime geçin.", "asset_skipped": "Atlandı", "asset_skipped_in_trash": "Çöpte", "asset_uploaded": "Yüklendi", "asset_uploading": "Yükleniyor...", "assets": "Varlıklar", + "assets_added_count": "{count, plural, one {# varlık eklendi} other {# varlık eklendi}}", + "assets_added_to_album_count": "{count, plural, one {# varlık} other {# varlık}} albüme eklendi", + "assets_added_to_name_count": "{count, plural, one {# varlık} other {# varlık}} {hasName, select, true {{name}} other {yeni albüm}} içine eklendi", "assets_count": "{say, çoğul, bir {#varlık} diğer {#varlık}}", "assets_moved_to_trash_count": "{say, çoğul, bir {#varlık} diğer {#varlık}} çöp kutusuna taşındı", - "assets_restore_confirmation": "Çöpteki bütün varlıkları geri yüklemek istediğinize emin misiniz? Bu işlem geri alınamaz!", + "assets_permanently_deleted_count": "Kalıcı olarak silindi {count, plural, one {# varlık} other {# varlıklar}}", + "assets_removed_count": "Kaldırıldı {count, plural, one {# varlık} other {# varlıklar}}", + "assets_restore_confirmation": "Tüm çöp kutusundaki varlıklarınızı geri yüklemek istediğinizden emin misiniz? Bu işlemi geri alamazsınız! Ayrıca, çevrim dışı olan varlıkların bu şekilde geri yüklenemeyeceğini unutmayın.", + "assets_restored_count": "{count, plural, one {# varlık} other {# varlıklar}} geri yüklendi", + "assets_trashed_count": "{count, plural, one {# varlık} other {# varlıklar}} çöp kutusuna taşındı", + "assets_were_part_of_album_count": "{count, plural, one {Varlık zaten} other {Varlıklar zaten}} albümün parçasıydı", "authorized_devices": "Yetki Verilmiş Cihazlar", "back": "Geri", - "back_close_deselect": "Geri, kapat, veya seçimi kaldır", - "backward": "", + "back_close_deselect": "Geri, kapat veya seçimi kaldır", + "backward": "Geriye doğru", "birthdate_saved": "Doğum günü başarılı bir şekilde kaydedildi", "birthdate_set_description": "Doğum günü, fotoğraftaki insanın fotoğraf çekildiği zamandaki yaşının hesaplanması için kullanılır.", "blurred_background": "Bulanık arkaplan", - "bulk_delete_duplicates_confirmation": "Toplu olarak {sayım, çoğul, bir {# yinelenen varlık} diğer {# yinelenen varlıklar} 'ı silmek istediğinizden emin misiniz? Bu, her grubun en büyük varlığını tutacak ve diğer tüm kopyaları kalıcı olarak silecektir. Bu işlemi geri alamazsın!", - "bulk_keep_duplicates_confirmation": "{sayım, çoğul, bir {# yinelenen varlık} diğer {# yinelenen varlıklar}}ı tutmak istediğinizden emin misiniz? Bu, hiçbir şeyi silmeden tüm yinelenen grupları çözecektir.", - "bulk_trash_duplicates_confirmation": "Toplu olarak {say, çoğul, bir {# yinelenen varlık} diğer {# yinelenen varlıklar} öğesini çöpe atmak istediğinizden emin misiniz? Bu, her grubun en büyük varlığını tutacak ve diğer tüm kopyaları çöpe atacaktır.", + "bugs_and_feature_requests": "Hatalar ve Özellik Talepleri", + "build": "Yapı", + "build_image": "Görüntü Oluştur", + "bulk_delete_duplicates_confirmation": "Toplu olarak {count, plural, one {# kopya öğeyi} other {# kopya öğeleri}} silmek istediğinizden emin misiniz? Bu işlem, her gruptaki en büyük öğeyi tutacak ve diğer tüm kopyaları kalıcı olarak silecektir. Bu işlemi geri alamazsınız!", + "bulk_keep_duplicates_confirmation": "{count, plural, one {# kopya öğeyi} other {# kopya öğeleri}} tutmak istediğinizden emin misiniz? Bu işlem, hiçbir şeyi silmeden tüm kopya gruplarını çözecektir.", + "bulk_trash_duplicates_confirmation": "{count, plural, one {# kopya öğeyi} other {# kopya öğeleri}} toplu olarak çöp kutusuna taşımak istediğinizden emin misiniz? Bu işlem, her grubun en büyük öğesini tutacak ve diğer tüm kopyaları çöp kutusuna taşıyacaktır.", "buy": "Immich'i Satın Alın", "camera": "Kamera", "camera_brand": "Kamera markası", @@ -435,7 +448,7 @@ "cant_search_people": "", "cant_search_places": "", "change_date": "Tarihi değiştir", - "change_expiration_time": "", + "change_expiration_time": "Son kullanma süresini değiştir", "change_location": "Konumu değiştir", "change_name": "İsim değiştir", "change_name_successfully": "İsim başarıyla değiştirildi", @@ -443,8 +456,8 @@ "change_password_description": "Bu ya sistemdeki ilk oturum açışınız ya da şifre değişikliği için bir talepte bulunuldu. Lütfen yeni şifreyi aşağıya yazınız.", "change_your_password": "Şifreni değiştir", "changed_visibility_successfully": "Görünürlük başarıyla değiştirildi", - "check_all": "", - "check_logs": "Logları Konrol et", + "check_all": "Tümünü Seç", + "check_logs": "Günlükleri Kontrol Et", "choose_matching_people_to_merge": "Birleştirmek için eşleşen kişileri seçiniz", "city": "Şehir", "clear": "Temiz", @@ -454,7 +467,8 @@ "clear_value": "Değeri Temizle", "clockwise": "Saat yönü", "close": "Kapat", - "collapse_all": "", + "collapse": "Daralt", + "collapse_all": "Tümünü Daralt", "color": "Renk", "color_theme": "Renk teması", "comment_deleted": "Yorum silindi", @@ -465,8 +479,8 @@ "confirm_admin_password": "Yönetici Şifresini Onayla", "confirm_delete_shared_link": "Bu paylaşılan bağlantıyı silmek istediğinizden emin misiniz?", "confirm_password": "Şifreyi onayla", - "contain": "", - "context": "", + "contain": "İçermek", + "context": "Bağlam", "continue": "Devam et", "copied_image_to_clipboard": "Resim, panoya kopyalandı.", "copied_to_clipboard": "Panoya kopyalandı!", @@ -478,34 +492,38 @@ "copy_password": "Parolayı kopyala", "copy_to_clipboard": "Panoya Kopyala", "country": "Ülke", - "cover": "", - "covers": "", + "cover": "Kapla", + "covers": "Kaplar", "create": "Oluştur", "create_album": "Albüm oluştur", "create_library": "Kütüphane Oluştur", "create_link": "Link oluştur", "create_link_to_share": "Paylaşmak için link oluştur", + "create_link_to_share_description": "Bağlantıya sahip olan herkesin seçilen fotoğrafları görmesine izin ver", "create_new_person": "Yeni kişi oluştur", "create_new_person_hint": "Seçili varlıkları yeni bir kişiye atayın", "create_new_user": "Yeni kullanıcı oluştur", "create_tag": "Etiket oluştur", + "create_tag_description": "Yeni bir etiket oluşturun. İç içe geçmiş etiketler için, etiketi tam yolu ve eğik çizgileri de dahil ederek giriniz.", "create_user": "Kullanıcı oluştur", "created": "Oluşturuldu", - "current_device": "", + "current_device": "Mevcut cihaz", "custom_locale": "Özel Yerel Ayar", "custom_locale_description": "Tarihleri ve sayıları dile ve bölgeye göre biçimlendirin", "dark": "Koyu", - "date_after": "", + "date_after": "Sonraki tarih", "date_and_time": "Tarih ve Zaman", - "date_before": "", + "date_before": "Önceki tarih", "date_of_birth_saved": "Doğum günü başarı ile kaydedildi", "date_range": "Tarih aralığı", "day": "Gün", - "default_locale": "", + "deduplicate_all": "Tüm kopyaları kaldır", + "default_locale": "Varsayılan Yerel Ayar", "default_locale_description": "Tarihleri ve sayıları tarayıcınızın yerel ayarına göre biçimlendirin", "delete": "Sil", "delete_album": "Albümü sil", "delete_api_key_prompt": "Bu API anahtarını silmek istediğinizden emin misiniz?", + "delete_duplicates_confirmation": "Bu kopyaları kalıcı olarak silmek istediğinizden emin misiniz?", "delete_key": "Anahtarı sil", "delete_library": "Kütüphaneyi sil", "delete_link": "Bağlantıyı sil", @@ -513,25 +531,34 @@ "delete_tag": "Etiketi sil", "delete_tag_confirmation_prompt": "{tagName} etiketini silmek istediğinizden emin misiniz?", "delete_user": "Kullanıcıyı sil", - "deleted_shared_link": "", + "deleted_shared_link": "Paylaşılan bağlantı silindi", + "deletes_missing_assets": "Diskte eksik olan varlıkları siler", "description": "Açıklama", "details": "Detaylar", "direction": "Yön", - "disabled": "", - "disallow_edits": "", + "disabled": "Devre dışı bırakıldı", + "disallow_edits": "Değişikliklere izin verme", + "discord": "Discord", "discover": "Keşfet", "dismiss_all_errors": "Tüm hataları yoksay", "dismiss_error": "Hatayı yoksay", "display_options": "Görüntüleme seçenekleri", - "display_order": "", + "display_order": "Gösterim sıralaması", "display_original_photos": "Orijinal fotoğrafları göster", - "display_original_photos_setting_description": "", + "display_original_photos_setting_description": "Orijinal varlık web uyumlu olduğunda, bir varlığı görüntülerken küçük resimler yerine orijinal fotoğrafı görüntülemeyi tercih edin. Bu, fotoğraf görüntüleme hızlarının yavaşlamasına neden olabilir.", "do_not_show_again": "Bu mesajı bir daha gösterme", - "done": "", + "documentation": "Dokümantasyon", + "done": "Bitti", "download": "İndir", + "download_include_embedded_motion_videos": "Gömülü videolar", + "download_include_embedded_motion_videos_description": "Görsel hareketli fotoğraflarda yer alan gömülü videoları ayrı bir dosya olarak dahil et", "download_settings": "İndir", - "downloading": "", - "duplicates": "", + "download_settings_description": "Varlık indirme ile ilgili ayarları yönetin", + "downloading": "İndiriliyor", + "downloading_asset_filename": "Varlık indiriliyor {filename}", + "drop_files_to_upload": "Dosyaları yüklemek için herhangi bir yere bırakın", + "duplicates": "Kopyalar", + "duplicates_description": "Her grubu çözmek için, varsa hangilerinin kopya olduğunu belirtin", "duration": "Süre", "durations": { "days": "", @@ -542,37 +569,47 @@ }, "edit": "Düzenle", "edit_album": "Albümü düzenle", - "edit_avatar": "", - "edit_date": "", - "edit_date_and_time": "", - "edit_exclusion_pattern": "", - "edit_faces": "", - "edit_import_path": "", - "edit_import_paths": "", + "edit_avatar": "Avatarı Düzenle", + "edit_date": "Tarihi Düzenle", + "edit_date_and_time": "Tarih ve zamanı düzenleyin", + "edit_exclusion_pattern": "Hariç tutma desenini düzenle", + "edit_faces": "Yüzleri Düzenleyin", + "edit_import_path": "İçe aktarma yolunu düzenleyin", + "edit_import_paths": "İçe Aktarma Yollarını Düzenle", "edit_key": "Anahtarı düzenle", "edit_link": "Bağlantıyı düzenle", - "edit_location": "", - "edit_name": "", + "edit_location": "Lokasyonu düzenleyin", + "edit_name": "İsmi düzenleyin", "edit_people": "Kişileri düzenle", "edit_tag": "Etiketi düzenle", "edit_title": "Başlığı düzenle", "edit_user": "Kullanıcıyı düzenle", - "edited": "", - "editor": "", + "edited": "Düzenlendi", + "editor": "Editör", "editor_close_without_save_prompt": "Değişiklikler kaydedilmeyecek", "editor_close_without_save_title": "Düzenleyici kapatılsın mı?", + "editor_crop_tool_h2_aspect_ratios": "En boy oranları", + "editor_crop_tool_h2_rotation": "Rotasyon", "email": "E-posta", "empty_album": "", "empty_trash": "Çöpü boşalt", + "empty_trash_confirmation": "Çöp kutusunu boşaltmak istediğinizden emin misiniz? Bu işlem, Immich'teki çöp kutusundaki tüm varlıkları kalıcı olarak silecektir.\nBu işlemi geri alamazsınız!", "enable": "Etkinleştir", "enabled": "Etkinleştirildi", - "end_date": "", + "end_date": "Bitiş tarihi", "error": "Hata", - "error_loading_image": "", + "error_loading_image": "Resim yüklenirken hata oluştu", + "error_title": "Bir Hata Oluştu - Bir şeyler ters gitti", "errors": { + "cannot_navigate_next_asset": "Sonraki varlığa geçiş yapılamıyor", + "cannot_navigate_previous_asset": "Önceki varlığa geçiş yapılamıyor", "cant_apply_changes": "Değişiklikler uygulanamıyor", + "cant_change_activity": "Etkinliği {etkinleştiremiyor, seçemiyor, doğru {devre dışı bırakamıyor} diğer durumda {etkinleştiremiyor}}", + "cant_change_asset_favorite": "Varlığın favori durumunu değiştiremiyor", + "cant_change_metadata_assets_count": "{count} varlığın metadatası (meta verisi) değiştirilemiyor", "cant_search_people": "Kişiler aranamıyor", - "cleared_jobs": "", + "cant_search_places": "Mekanlar aranamıyor", + "cleared_jobs": "İşler temizlendi: {job}", "exclusion_pattern_already_exists": "", "failed_job_command": "", "failed_to_create_album": "Albüm oluşturulamadı", diff --git a/web/src/lib/i18n/uk.json b/i18n/uk.json similarity index 99% rename from web/src/lib/i18n/uk.json rename to i18n/uk.json index 5d41b69cf1..8e3d05ceb6 100644 --- a/web/src/lib/i18n/uk.json +++ b/i18n/uk.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Апаратне прискорення", "transcoding_hardware_acceleration_description": "Експериментальний режим: значно швидший, але при однаковому бітрейті може мати меншу якість", "transcoding_hardware_decoding": "Апаратне декодування", - "transcoding_hardware_decoding_setting_description": "Застосовується тільки до NVENC, QSV і RKMPP. Вмикає наскрізне прискорення на заміну лише прискорення кодування. Може працювати не з усіма відео.", + "transcoding_hardware_decoding_setting_description": "Увімкнення наскрізного прискорення замість прискорення лише кодування. Може не працювати для всіх відео.", "transcoding_hevc_codec": "Кодек HEVC", "transcoding_max_b_frames": "Максимальна кількість проміжних кадрів", "transcoding_max_b_frames_description": "Вищі значення покращують ефективність стиснення, але збільшують час кодування. Можуть бути несумісні з апаратним прискоренням на старих пристроях. Значення 0 вимикає B-фрейми, а -1 автоматично налаштовує це значення.", @@ -887,6 +887,7 @@ "look": "Дивитися", "loop_videos": "Циклічні відео", "loop_videos_description": "Увімкнути циклічне відтворення відео.", + "main_branch_warning": "Ви використовуєте версію для розробників; ми настійно рекомендуємо використовувати релізну версію!", "make": "Виробник", "manage_shared_links": "Керування спільними посиланнями", "manage_sharing_with_partners": "Керуйте спільним використанням з партнерами", diff --git a/web/src/lib/i18n/vi.json b/i18n/vi.json similarity index 99% rename from web/src/lib/i18n/vi.json rename to i18n/vi.json index 7965f6202c..2152814fb9 100644 --- a/web/src/lib/i18n/vi.json +++ b/i18n/vi.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "Tăng tốc phần cứng", "transcoding_hardware_acceleration_description": "(Thử nghiệm) nhanh hơn nhiều nhưng sẽ có chất lượng thấp hơn ở cùng bitrate", "transcoding_hardware_decoding": "Giải mã phần cứng", - "transcoding_hardware_decoding_setting_description": "Chỉ áp dụng cho NVENC, QSV và RKMPP. Kích hoạt tăng tốc toàn bộ quá trình xử lý video chứ không chỉ là mã hóa. Điều này có thể không áp dụng được cho mọi video.", + "transcoding_hardware_decoding_setting_description": "Cho phép tăng tốc đầu cuối thay vì chỉ tăng tốc mã hóa. Có thể không hoạt động trên tất cả video.", "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Số B-frame tối đa", "transcoding_max_b_frames_description": "Giá trị cao hơn cải thiện hiệu quả nén, nhưng làm chậm mã hóa. Có thể không tương thích với tăng tốc phần cứng trên các thiết bị cũ. Giá trị 0 để tắt B-frames, trong khi giá trị -1 để tự động thiết lập giá trị này.", @@ -859,6 +859,7 @@ "look": "Xem", "loop_videos": "Lặp video", "loop_videos_description": "Bật để video tự động lặp lại trong trình xem chi tiết.", + "main_branch_warning": "Bạn đang dùng phiên bản đang phát triển; chúng tôi khuyên bạn nên dùng phiên bản phát hành!", "make": "Thương hiệu", "manage_shared_links": "Quản lý liên kết chia sẻ", "manage_sharing_with_partners": "Quản lý chia sẻ với người thân", diff --git a/web/src/lib/i18n/zh_Hant.json b/i18n/zh_Hant.json similarity index 97% rename from web/src/lib/i18n/zh_Hant.json rename to i18n/zh_Hant.json index c5ccf0ea1a..8b1e1e5e15 100644 --- a/web/src/lib/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -1,11 +1,11 @@ { "about": "關於", - "account": "帳號", - "account_settings": "帳號設定", + "account": "賬號", + "account_settings": "賬號設定", "acknowledge": "收到", - "action": "行爲", - "actions": "行爲", - "active": "處理中", + "action": "操作", + "actions": "操作", + "active": "活躍", "activity": "活動", "activity_changed": "活動已{enabled, select, true {啟用} other {停用}}", "add": "新增", @@ -14,31 +14,31 @@ "add_a_name": "新增名稱", "add_a_title": "新增標題", "add_exclusion_pattern": "新增排除規則", - "add_import_path": "新增匯入路徑", + "add_import_path": "新增引入路徑", "add_location": "新增地點", "add_more_users": "新增更多使用者", "add_partner": "新增同伴", "add_path": "新增路徑", - "add_photos": "加入照片", + "add_photos": "增加照片", "add_to": "新增至…", "add_to_album": "加入相簿", "add_to_shared_album": "加入共享相簿", - "added_to_archive": "已加入封存", + "added_to_archive": "已加入歸檔", "added_to_favorites": "已加入收藏", "added_to_favorites_count": "已把 {count, number} 個項目加入收藏", "admin": { - "add_exclusion_pattern_description": "新增排除規則。支援使用「*」、「 **」、「?」來匹配字串。如果要排除所有名稱為「Raw」的檔案或目錄,請使用「**/Raw/**」。如果要排除所有「.tif」結尾的檔案,請使用「**/*.tif」。如果要排除某個絕對路徑,請使用「/path/to/ignore/**」。", - "asset_offline_description": "磁碟上找不到此外部圖庫檔案,且已移至垃圾桶。如果檔案在圖庫內移動,請檢查時間軸中是否有新的相應的檔案。若要還原這份檔案,請確保 Immich 可以存取下列檔案路徑,並掃描圖庫。", + "add_exclusion_pattern_description": "新增排除規則。支援使用「*」、「 **」、「?」來匹配字串。如果要在任何名為「Raw」的目錄內排除所有條目,請使用「**/Raw/**」。如果要排除所有「.tif」結尾的檔案,請使用「**/*.tif」。如果要排除某個絕對路徑,請使用「/path/to/ignore/**」。", + "asset_offline_description": "磁碟上找不到此外部圖庫檔案,且已移至垃圾桶。如果檔案在圖庫內被移動,請檢查時間軸中是否有新的相應的檔案。若要還原這份檔案,請確保 Immich 可以寫入下列檔案路徑,並讀取掃描圖庫內容。", "authentication_settings": "驗證設定", "authentication_settings_description": "管理密碼、OAuth 與其他驗證設定", "authentication_settings_disable_all": "確定要停用所有登入方式嗎?這樣會完全無法登入。", "authentication_settings_reenable": "如需重新啟用,請使用 伺服器指令。", "background_task_job": "背景任務", "check_all": "全選", - "cleared_jobs": "已清除的作業:{job}", - "config_set_by_file": "目前的設定已透過設定檔案設置", + "cleared_jobs": "已為「{job}」清除作業", + "config_set_by_file": "目前的設定已透過配置文檔調整", "confirm_delete_library": "確定要刪除「{library}」(圖庫)嗎?", - "confirm_delete_library_assets": "您確定要刪除此圖庫嗎?這將從 Immich 中刪除{count, plural, one {個項目} other {個項目}},且無法復原。檔案仍會保留在硬碟中。", + "confirm_delete_library_assets": "您確定要移除此圖庫嗎?這將從 Immich 中刪除{count, plural, one {個項目} other {個項目}},且無法復原。檔案仍會保留在硬碟中。", "confirm_email_below": "請在底下輸入 {email} 來確認", "confirm_reprocess_all_faces": "確定要重新處理所有臉孔嗎?這會清除已命名的人物。", "confirm_user_password_reset": "您確定要重設 {user} 的密碼嗎?", @@ -51,8 +51,8 @@ "external_library_created_at": "外部圖庫(於 {date} 建立)", "external_library_management": "外部圖庫管理", "face_detection": "臉孔偵測", - "face_detection_description": "使用機器學習檢測資料中的人臉。影片檔只會偵測縮圖。選擇「重新整理」將重新處理所有資料。選擇「重設」將把尚未處理的資料加入處理佇列中。被檢測到的人臉將在所有人臉檢測完成後,排入人臉識別佇列中,並將它們分配到現有或新的人物中。", - "facial_recognition_job_description": "將檢測到的人臉分組到人物中。此步驟將在人臉檢測完成後運行。選擇「重設」將重新分類所有人臉。選擇「缺失」將把沒有分配人物的人臉排入佇列。", + "face_detection_description": "使用機器學習偵測檔案中的臉孔(影片只會偵測縮圖中的臉孔)。選擇「重新整理」會重新處理所有檔案。選擇「重設」會清除目前所有的臉孔資料。選擇「遺失的」會把尚未處理的檔案排入處理佇列。臉孔偵測完成後,會把偵測到的臉孔排入臉部辨識佇列,將其分組到現有的或新的人物中。", + "facial_recognition_job_description": "將偵測到的臉孔依照人物分組。此步驟會在臉孔偵測完成後執行。選擇「重設」會重新分組所有臉孔。選擇「遺失的」會把尚未指定人物的臉孔排入佇列。", "failed_job_command": "{job} 任務的 {command} 指令執行失敗", "force_delete_user_warning": "警告:這將立即移除使用者及其資料。操作後無法反悔且移除的檔案無法恢復。", "forcing_refresh_library_files": "強制重新整理所有圖庫檔案", @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "硬體加速", "transcoding_hardware_acceleration_description": "實驗性功能;速度更快,但在相同比特率下質量較低", "transcoding_hardware_decoding": "硬體解碼", - "transcoding_hardware_decoding_setting_description": "僅適用於 NVENC、QSV 和 RKMPP。啟用端到端加速,而不僅僅是加速編碼。可能並非所有視頻都適用。", + "transcoding_hardware_decoding_setting_description": "不只加速編碼,還啟用端對端加速。可能不支援某些影片。", "transcoding_hevc_codec": "HEVC 編解碼器", "transcoding_max_b_frames": "最大 B 幀數", "transcoding_max_b_frames_description": "更高的值可以提高壓縮效率,但會降低編碼速度。在舊設備上可能不兼容硬件加速。0 表示禁用 B 幀,而 -1 則會自動設置此值。", @@ -403,7 +403,7 @@ "asset_adding_to_album": "加入相簿中…", "asset_description_updated": "檔案描述已更新", "asset_filename_is_offline": "檔案 {filename} 離線了", - "asset_has_unassigned_faces": "檔案有未分配的面孔", + "asset_has_unassigned_faces": "檔案中有未指定的臉孔", "asset_hashing": "Hashing中...", "asset_offline": "檔案離線", "asset_offline_description": "磁碟中找不到此外部檔案。請向您的 Immich 管理員尋求協助。", @@ -749,7 +749,7 @@ "extension": "副檔名", "external": "外部", "external_libraries": "外部圖庫", - "face_unassigned": "未指派", + "face_unassigned": "未指定", "failed_to_get_people": "", "favorite": "收藏", "favorite_or_unfavorite_photo": "收藏或取消收藏照片", @@ -858,6 +858,7 @@ "look": "樣貌", "loop_videos": "重播影片", "loop_videos_description": "啟用後,影片結束會自動重播。", + "main_branch_warning": "現在使用的是開發版本;我們強烈建議使用正式發行版!", "make": "製造商", "manage_shared_links": "管理分享鏈結", "manage_sharing_with_partners": "管理與夥伴的分享", @@ -1052,9 +1053,9 @@ "raw": "", "reaction_options": "反應選項", "read_changelog": "閱覽變更日誌", - "reassign": "重新指派", - "reassigned_assets_to_existing_person": "已將 {count, plural, one {# 個檔案} other {# 個檔案}} 重新分配給 {name, select, null {現有的人} other {{name}}}", - "reassigned_assets_to_new_person": "已將 {count, plural, one {# 個檔案} other {# 個檔案}} 重新分配給一位新的使用者", + "reassign": "重新指定", + "reassigned_assets_to_existing_person": "已將 {count, plural, other {# 個檔案}}重新指定給{name, select, null {現有的人} other {{name}}}", + "reassigned_assets_to_new_person": "已將 {count, plural, other {# 個檔案}}重新指定給一位新人物", "reassing_hint": "將選定的檔案分配給己存在的人物", "recent": "最近", "recent_searches": "最近搜尋項目", diff --git a/web/src/lib/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json similarity index 99% rename from web/src/lib/i18n/zh_SIMPLIFIED.json rename to i18n/zh_SIMPLIFIED.json index 6701d59ed1..c2e1a87350 100644 --- a/web/src/lib/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -287,7 +287,7 @@ "transcoding_hardware_acceleration": "硬件加速", "transcoding_hardware_acceleration_description": "(实验性功能)速度更快,但在相同码率下质量会降低", "transcoding_hardware_decoding": "硬件解码", - "transcoding_hardware_decoding_setting_description": "仅适用于NVENC、QSV和RKMPP。启用端到端加速,而不仅仅是加速编码。可能并不适用于所有视频。", + "transcoding_hardware_decoding_setting_description": "启用端到端加速,而不仅仅是加速编码。可能并不适用于所有视频。", "transcoding_hevc_codec": "HEVC 编解码器", "transcoding_max_b_frames": "最大B帧数", "transcoding_max_b_frames_description": "较高的值可以提高压缩效率,但会减慢编码速度。可能与旧设备上的硬件加速不兼容。0表示将禁用B帧,-1表示将自动设置此参数。", @@ -307,7 +307,7 @@ "transcoding_settings_description": "管理视频文件的分辨率和编码信息", "transcoding_target_resolution": "目标分辨率", "transcoding_target_resolution_description": "更高的分辨率可以保留更多细节,但编码时间更长,文件体积更大,且可能降低应用程序的响应速度。", - "transcoding_temporal_aq": "Temporal AQ", + "transcoding_temporal_aq": "时间自适应量化", "transcoding_temporal_aq_description": "仅适用于NVENC。提高高细节、低动态场景的质量。可能与旧设备不兼容。", "transcoding_threads": "线程数", "transcoding_threads_description": "设定值越高,编码速度越快,留给其它任务(Docker外宿主机的任务等)的计算能力越少。此值不应大于CPU核心的数量。0表示最大限度地提高利用率。", @@ -385,10 +385,10 @@ "allow_public_user_to_download": "允许所有用户下载", "allow_public_user_to_upload": "允许所有用户上传", "anti_clockwise": "逆时针", - "api_key": "API Key", + "api_key": "API 密钥", "api_key_description": "该应用密钥只会展示一次。请确保在关闭窗口前复制下来。", "api_key_empty": "API Key的名称不可以为空", - "api_keys": "API Keys", + "api_keys": "API 密钥", "app_settings": "应用设置", "appears_in": "出现于", "archive": "归档", @@ -888,6 +888,7 @@ "look": "样式", "loop_videos": "循环视频", "loop_videos_description": "启用在详细信息中自动循环播放视频。", + "main_branch_warning": "您当前使用的是开发版;我们强烈建议您使用正式发行版(release版)!", "make": "品牌", "manage_shared_links": "管理共享链接", "manage_sharing_with_partners": "管理与同伴的共享", @@ -1330,7 +1331,7 @@ "unnamed_album": "未命名相册", "unnamed_album_delete_confirmation": "您确定要删除该相册吗?", "unnamed_share": "未命名共享", - "unsaved_change": "未保存的修改", + "unsaved_change": "修改未保存", "unselect_all": "取消全选", "unselect_all_duplicates": "取消选择所有重复项", "unstack": "取消堆叠", @@ -1369,7 +1370,7 @@ "version_announcement_closing": "你的朋友,Alex", "version_announcement_message": "嗨,朋友,当前应用出新版本了,请抽空阅读一下发行说明,并及时更新你的docker-compose.yml.env文件,避免存在配置错误,特别是当你是使用WatchTower或其它类似的自动升级工具时。", "version_history": "版本历史", - "version_history_item": "在{date}安装版本{version}", + "version_history_item": "在 {date} 安装版本 {version}", "video": "视频", "video_hover_setting": "鼠标悬停时播放视频缩略图", "video_hover_setting_description": "当鼠标悬停在项目上时播放视频缩略图。即使禁用了这个功能,也可以通过将鼠标悬停在播放图标上来开始播放。", diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index 155d78f4a3..fa654d70b7 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:3cdce69fd5663ca47c420ec4d4df8e3545519a4030372f7d2064fb1be2279844 AS builder-cpu +FROM python:3.11-bookworm@sha256:70f1eb2927a8ef72840254b17024d3a8aa8c3c9715a625d426a2861b5899bc62 AS builder-cpu FROM builder-cpu AS builder-openvino @@ -34,7 +34,7 @@ RUN python3 -m venv /opt/venv COPY poetry.lock pyproject.toml ./ RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev -FROM python:3.11-slim-bookworm@sha256:5501a4fe605abe24de87c2f3d6cf9fd760354416a0cad0296cf284fddcdca9e2 AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:5148c0e4bbb64271bca1d3322360ebf4bfb7564507ae32dd639322e4952a6b16 AS prod-cpu FROM prod-cpu AS prod-openvino diff --git a/machine-learning/app/config.py b/machine-learning/app/config.py index 828dee15f0..92799ac692 100644 --- a/machine-learning/app/config.py +++ b/machine-learning/app/config.py @@ -19,6 +19,10 @@ class PreloadModelData(BaseModel): facial_recognition: str | None = None +class MaxBatchSize(BaseModel): + facial_recognition: int | None = None + + class Settings(BaseSettings): model_config = SettingsConfigDict( env_prefix="MACHINE_LEARNING_", @@ -41,6 +45,7 @@ class Settings(BaseSettings): ann_fp16_turbo: bool = False ann_tuning_level: int = 2 preload: PreloadModelData | None = None + max_batch_size: MaxBatchSize | None = None @property def device_id(self) -> str: diff --git a/machine-learning/app/models/facial_recognition/recognition.py b/machine-learning/app/models/facial_recognition/recognition.py index c060bdd616..dcfb6b530e 100644 --- a/machine-learning/app/models/facial_recognition/recognition.py +++ b/machine-learning/app/models/facial_recognition/recognition.py @@ -3,13 +3,14 @@ from typing import Any import numpy as np import onnx +import onnxruntime as ort from insightface.model_zoo import ArcFaceONNX from insightface.utils.face_align import norm_crop from numpy.typing import NDArray from onnx.tools.update_model_dims import update_inputs_outputs_dims from PIL import Image -from app.config import log +from app.config import log, settings from app.models.base import InferenceModel from app.models.transforms import decode_cv2 from app.schemas import FaceDetectionOutput, FacialRecognitionOutput, ModelFormat, ModelSession, ModelTask, ModelType @@ -22,11 +23,12 @@ class FaceRecognizer(InferenceModel): def __init__(self, model_name: str, min_score: float = 0.7, **model_kwargs: Any) -> None: super().__init__(model_name, **model_kwargs) self.min_score = model_kwargs.pop("minScore", min_score) - self.batch = self.model_format == ModelFormat.ONNX + max_batch_size = settings.max_batch_size.facial_recognition if settings.max_batch_size else None + self.batch_size = max_batch_size if max_batch_size else self._batch_size_default def _load(self) -> ModelSession: session = self._make_session(self.model_path) - if self.batch and str(session.get_inputs()[0].shape[0]) != "batch": + if (not self.batch_size or self.batch_size > 1) and str(session.get_inputs()[0].shape[0]) != "batch": self._add_batch_axis(self.model_path) session = self._make_session(self.model_path) self.model = ArcFaceONNX( @@ -42,18 +44,18 @@ class FaceRecognizer(InferenceModel): return [] inputs = decode_cv2(inputs) cropped_faces = self._crop(inputs, faces) - embeddings = self._predict_batch(cropped_faces) if self.batch else self._predict_single(cropped_faces) + embeddings = self._predict_batch(cropped_faces) return self.postprocess(faces, embeddings) def _predict_batch(self, cropped_faces: list[NDArray[np.uint8]]) -> NDArray[np.float32]: - embeddings: NDArray[np.float32] = self.model.get_feat(cropped_faces) - return embeddings + if not self.batch_size or len(cropped_faces) <= self.batch_size: + embeddings: NDArray[np.float32] = self.model.get_feat(cropped_faces) + return embeddings - def _predict_single(self, cropped_faces: list[NDArray[np.uint8]]) -> NDArray[np.float32]: - embeddings: list[NDArray[np.float32]] = [] - for face in cropped_faces: - embeddings.append(self.model.get_feat(face)) - return np.concatenate(embeddings, axis=0) + batch_embeddings: list[NDArray[np.float32]] = [] + for i in range(0, len(cropped_faces), self.batch_size): + batch_embeddings.append(self.model.get_feat(cropped_faces[i : i + self.batch_size])) + return np.concatenate(batch_embeddings, axis=0) def postprocess(self, faces: FaceDetectionOutput, embeddings: NDArray[np.float32]) -> FacialRecognitionOutput: return [ @@ -77,3 +79,8 @@ class FaceRecognizer(InferenceModel): output_dims = {proto.graph.output[0].name: ["batch"] + static_output_dims} updated_proto = update_inputs_outputs_dims(proto, input_dims, output_dims) onnx.save(updated_proto, model_path) + + @property + def _batch_size_default(self) -> int | None: + providers = ort.get_available_providers() + return None if self.model_format == ModelFormat.ONNX and "OpenVINOExecutionProvider" not in providers else 1 diff --git a/machine-learning/app/test_main.py b/machine-learning/app/test_main.py index 50ec188aa4..e5cb63997c 100644 --- a/machine-learning/app/test_main.py +++ b/machine-learning/app/test_main.py @@ -549,7 +549,7 @@ class TestFaceRecognition: face_recognizer = FaceRecognizer("buffalo_s", cache_dir=path) face_recognizer.load() - assert face_recognizer.batch is True + assert face_recognizer.batch_size is None update_dims.assert_called_once_with(proto, {"input.1": ["batch", 3, 224, 224]}, {"output.1": ["batch", 800]}) onnx.save.assert_called_once_with(update_dims.return_value, face_recognizer.model_path) @@ -572,7 +572,7 @@ class TestFaceRecognition: face_recognizer = FaceRecognizer("buffalo_s", cache_dir=path) face_recognizer.load() - assert face_recognizer.batch is True + assert face_recognizer.batch_size is None update_dims.assert_not_called() onnx.load.assert_not_called() onnx.save.assert_not_called() @@ -596,7 +596,33 @@ class TestFaceRecognition: face_recognizer = FaceRecognizer("buffalo_s", model_format=ModelFormat.ARMNN, cache_dir=path) face_recognizer.load() - assert face_recognizer.batch is False + assert face_recognizer.batch_size == 1 + update_dims.assert_not_called() + onnx.load.assert_not_called() + onnx.save.assert_not_called() + + def test_recognition_does_not_add_batch_axis_for_openvino( + self, ort_session: mock.Mock, path: mock.Mock, mocker: MockerFixture + ) -> None: + onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True) + update_dims = mocker.patch( + "app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True + ) + mocker.patch("app.models.base.InferenceModel.download") + mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX") + path.return_value.__truediv__.return_value.__truediv__.return_value.suffix = ".onnx" + + inputs = [SimpleNamespace(name="input.1", shape=("batch", 3, 224, 224))] + outputs = [SimpleNamespace(name="output.1", shape=("batch", 800))] + ort_session.return_value.get_inputs.return_value = inputs + ort_session.return_value.get_outputs.return_value = outputs + + face_recognizer = FaceRecognizer( + "buffalo_s", model_format=ModelFormat.ARMNN, cache_dir=path, providers=["OpenVINOExecutionProvider"] + ) + face_recognizer.load() + + assert face_recognizer.batch_size == 1 update_dims.assert_not_called() onnx.load.assert_not_called() onnx.save.assert_not_called() diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock index c1ceabab61..fa5cb2d9c2 100644 --- a/machine-learning/poetry.lock +++ b/machine-learning/poetry.lock @@ -147,6 +147,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -159,8 +163,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -171,8 +181,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -182,6 +208,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -193,6 +223,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -205,6 +239,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -217,6 +255,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -235,63 +277,78 @@ files = [ [[package]] name = "cffi" -version = "1.16.0" +version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] [package.dependencies] @@ -691,18 +748,18 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi-slim" -version = "0.115.0" +version = "0.115.2" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi_slim-0.115.0-py3-none-any.whl", hash = "sha256:27ab44da95b622e68be7a19f06df1960a320b9d94e689b0adfc055bb26ee9be7"}, - {file = "fastapi_slim-0.115.0.tar.gz", hash = "sha256:b4b962ca2aa0a31010dafdad3d4da99d368a5591223304c6fb385712fad7feb6"}, + {file = "fastapi_slim-0.115.2-py3-none-any.whl", hash = "sha256:7dd65a58e9b2a1558ebd479d35e621417d4572188d902a3e5564739914ddc81d"}, + {file = "fastapi_slim-0.115.2.tar.gz", hash = "sha256:83a1ce1e385029cad305b49dcc03622d2be6a905b2ab65d2e7096285605cca8d"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.39.0" +starlette = ">=0.37.2,<0.41.0" typing-extensions = ">=4.8.0" [package.extras] @@ -903,59 +960,54 @@ wcwidth = "*" [[package]] name = "gevent" -version = "23.9.1" +version = "24.10.3" description = "Coroutine-based network library" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "gevent-23.9.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771"}, - {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69"}, - {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535"}, - {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a"}, - {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39"}, - {file = "gevent-23.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae"}, - {file = "gevent-23.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6"}, - {file = "gevent-23.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7"}, - {file = "gevent-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e"}, - {file = "gevent-23.9.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c"}, - {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648"}, - {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07"}, - {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9"}, - {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011"}, - {file = "gevent-23.9.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7"}, - {file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71"}, - {file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e"}, - {file = "gevent-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a"}, - {file = "gevent-23.9.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904"}, - {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b"}, - {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e"}, - {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599"}, - {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea"}, - {file = "gevent-23.9.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599"}, - {file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303"}, - {file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d"}, - {file = "gevent-23.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1"}, - {file = "gevent-23.9.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe"}, - {file = "gevent-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5"}, - {file = "gevent-23.9.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397"}, - {file = "gevent-23.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507"}, - {file = "gevent-23.9.1-cp38-cp38-win32.whl", hash = "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a"}, - {file = "gevent-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f"}, - {file = "gevent-23.9.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a"}, - {file = "gevent-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653"}, - {file = "gevent-23.9.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd"}, - {file = "gevent-23.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543"}, - {file = "gevent-23.9.1-cp39-cp39-win32.whl", hash = "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2"}, - {file = "gevent-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b"}, - {file = "gevent-23.9.1.tar.gz", hash = "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34"}, + {file = "gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e"}, + {file = "gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18"}, + {file = "gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13"}, + {file = "gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba"}, + {file = "gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328"}, + {file = "gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7"}, + {file = "gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26"}, + {file = "gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290"}, + {file = "gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824"}, + {file = "gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e"}, + {file = "gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4"}, + {file = "gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99"}, + {file = "gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c"}, + {file = "gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861"}, + {file = "gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec"}, + {file = "gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015"}, + {file = "gevent-24.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70e9ed7ecb70e0df7dc97c3bc420de9a45a7c76bd5861c6cfec8c549700e681e"}, + {file = "gevent-24.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3ac83b74304487afa211a01909c7dd257e574db0cd429d866c298e21df7aeedf"}, + {file = "gevent-24.10.3-cp39-cp39-win32.whl", hash = "sha256:a9a89d6e396ef6f1e3968521bf56e8c4bee25b193bbf5d428b7782d582410822"}, + {file = "gevent-24.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:40ea3e40e8bb4fdb143c2a8edf2ccfdebd56016c7317c341ce8094c7bee08818"}, + {file = "gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18"}, + {file = "gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1"}, ] [package.dependencies] -cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} -greenlet = [ - {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, - {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, -] +cffi = {version = ">=1.17.1", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} +greenlet = {version = ">=3.1.1", markers = "platform_python_implementation == \"CPython\""} "zope.event" = "*" "zope.interface" = "*" @@ -963,8 +1015,8 @@ greenlet = [ dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] -test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests", "setuptools"] +recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] +test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] [[package]] name = "geventhttpclient" @@ -1051,69 +1103,84 @@ examples = ["oauth2"] [[package]] name = "greenlet" -version = "3.0.3" +version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, ] [package.extras] @@ -1542,13 +1609,13 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"] [[package]] name = "locust" -version = "2.31.8" +version = "2.32.0" description = "Developer-friendly load testing framework" optional = false python-versions = ">=3.9" files = [ - {file = "locust-2.31.8-py3-none-any.whl", hash = "sha256:4194e3d4a0472f1206c51532ed527017f3da1a7d1037ca4b2f0735d5dcd2f78f"}, - {file = "locust-2.31.8.tar.gz", hash = "sha256:b240c0d3e1724317d9211e81e99fbe42a3469071ef4d34d2ae6a727776d56377"}, + {file = "locust-2.32.0-py3-none-any.whl", hash = "sha256:e004514332b8631ca91382d11d224baee4ced040c5f5c8b2233800ebcbc73c0e"}, + {file = "locust-2.32.0.tar.gz", hash = "sha256:d8f7f5d9d4e801b2e7b0ee3f31109333673da744ccedf85e7da0151f2d263dd9"}, ] [package.dependencies] @@ -1556,7 +1623,10 @@ ConfigArgParse = ">=1.5.5" flask = ">=2.0.0" Flask-Cors = ">=3.0.10" Flask-Login = ">=0.6.3" -gevent = ">=22.10.2" +gevent = [ + {version = ">=22.10.2", markers = "python_full_version <= \"3.12.0\""}, + {version = ">=24.10.1", markers = "python_full_version > \"3.13.0\""}, +] geventhttpclient = ">=2.3.1" msgpack = ">=1.0.0" psutil = ">=5.9.1" @@ -1806,38 +1876,43 @@ files = [ [[package]] name = "mypy" -version = "1.11.2" +version = "1.12.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, - {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, - {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, - {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, - {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, - {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, - {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, - {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, - {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, - {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, - {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, - {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, - {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, - {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, - {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, - {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, - {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, - {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, - {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, + {file = "mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801"}, + {file = "mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5"}, + {file = "mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1"}, + {file = "mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627"}, + {file = "mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20"}, + {file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"}, + {file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"}, + {file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"}, + {file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"}, + {file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"}, + {file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"}, + {file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"}, + {file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"}, + {file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"}, + {file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"}, + {file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"}, + {file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"}, + {file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"}, + {file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"}, + {file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"}, + {file = "mypy-1.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b16fe09f9c741d85a2e3b14a5257a27a4f4886c171d562bc5a5e90d8591906b8"}, + {file = "mypy-1.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0dcc1e843d58f444fce19da4cce5bd35c282d4bde232acdeca8279523087088a"}, + {file = "mypy-1.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10ba7de5c616e44ad21005fa13450cd0de7caaa303a626147d45307492e4f2d"}, + {file = "mypy-1.12.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e6fe449223fa59fbee351db32283838a8fee8059e0028e9e6494a03802b4004"}, + {file = "mypy-1.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:dc6e2a2195a290a7fd5bac3e60b586d77fc88e986eba7feced8b778c373f9afe"}, + {file = "mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19"}, + {file = "mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e"}, + {file = "mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d"}, + {file = "mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd"}, + {file = "mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810"}, + {file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"}, + {file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"}, ] [package.dependencies] @@ -2509,13 +2584,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.5.2" +version = "2.6.0" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.5.2-py3-none-any.whl", hash = "sha256:2c912e55fd5794a59bf8c832b9de832dcfdf4778d79ff79b708744eed499a907"}, - {file = "pydantic_settings-2.5.2.tar.gz", hash = "sha256:f90b139682bee4d2065273d5185d71d37ea46cfe57e1b5ae184fc6a0b2484ca0"}, + {file = "pydantic_settings-2.6.0-py3-none-any.whl", hash = "sha256:4a819166f119b74d7f8c765196b165f95cc7487ce58ea27dec8a5a26be0970e0"}, + {file = "pydantic_settings-2.6.0.tar.gz", hash = "sha256:44a1804abffac9e6a30372bb45f6cafab945ef5af25e66b1c634c01dd39e0188"}, ] [package.dependencies] @@ -2928,29 +3003,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.6.9" +version = "0.7.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.9-py3-none-linux_armv6l.whl", hash = "sha256:064df58d84ccc0ac0fcd63bc3090b251d90e2a372558c0f057c3f75ed73e1ccd"}, - {file = "ruff-0.6.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:140d4b5c9f5fc7a7b074908a78ab8d384dd7f6510402267bc76c37195c02a7ec"}, - {file = "ruff-0.6.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53fd8ca5e82bdee8da7f506d7b03a261f24cd43d090ea9db9a1dc59d9313914c"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645d7d8761f915e48a00d4ecc3686969761df69fb561dd914a773c1a8266e14e"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eae02b700763e3847595b9d2891488989cac00214da7f845f4bcf2989007d577"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d5ccc9e58112441de8ad4b29dcb7a86dc25c5f770e3c06a9d57e0e5eba48829"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:417b81aa1c9b60b2f8edc463c58363075412866ae4e2b9ab0f690dc1e87ac1b5"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c866b631f5fbce896a74a6e4383407ba7507b815ccc52bcedabb6810fdb3ef7"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b118afbb3202f5911486ad52da86d1d52305b59e7ef2031cea3425142b97d6f"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67267654edc23c97335586774790cde402fb6bbdb3c2314f1fc087dee320bfa"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3ef0cc774b00fec123f635ce5c547dac263f6ee9fb9cc83437c5904183b55ceb"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:12edd2af0c60fa61ff31cefb90aef4288ac4d372b4962c2864aeea3a1a2460c0"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:55bb01caeaf3a60b2b2bba07308a02fca6ab56233302406ed5245180a05c5625"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:925d26471fa24b0ce5a6cdfab1bb526fb4159952385f386bdcc643813d472039"}, - {file = "ruff-0.6.9-py3-none-win32.whl", hash = "sha256:eb61ec9bdb2506cffd492e05ac40e5bc6284873aceb605503d8494180d6fc84d"}, - {file = "ruff-0.6.9-py3-none-win_amd64.whl", hash = "sha256:785d31851c1ae91f45b3d8fe23b8ae4b5170089021fbb42402d811135f0b7117"}, - {file = "ruff-0.6.9-py3-none-win_arm64.whl", hash = "sha256:a9641e31476d601f83cd602608739a0840e348bda93fec9f1ee816f8b6798b93"}, - {file = "ruff-0.6.9.tar.gz", hash = "sha256:b076ef717a8e5bc819514ee1d602bbdca5b4420ae13a9cf61a0c0a4f53a2baa2"}, + {file = "ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628"}, + {file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"}, + {file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11"}, + {file = "ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec"}, + {file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"}, + {file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"}, + {file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"}, ] [[package]] @@ -3363,13 +3438,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.31.1" +version = "0.32.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.31.1-py3-none-any.whl", hash = "sha256:adc42d9cac80cf3e51af97c1851648066841e7cfb6993a4ca8de29ac1548ed41"}, - {file = "uvicorn-0.31.1.tar.gz", hash = "sha256:f5167919867b161b7bcaf32646c6a94cdbd4c3aa2eb5c17d36bb9aa5cfd8c493"}, + {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"}, + {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"}, ] [package.dependencies] diff --git a/mobile/lib/interfaces/file_media.interface.dart b/mobile/lib/interfaces/file_media.interface.dart index c898183d79..ea01819dc3 100644 --- a/mobile/lib/interfaces/file_media.interface.dart +++ b/mobile/lib/interfaces/file_media.interface.dart @@ -10,6 +10,12 @@ abstract interface class IFileMediaRepository { String? relativePath, }); + Future saveImageWithFile( + String filePath, { + String? title, + String? relativePath, + }); + Future saveVideo( File file, { required String title, diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 40eda30204..48bc936a82 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -83,7 +83,7 @@ Future initApp() async { }; PlatformDispatcher.instance.onError = (error, stack) { - debugPrint("FlutterError - Catch all: $error"); + debugPrint("FlutterError - Catch all: $error \n $stack"); log.severe('PlatformDispatcher - Catch all', error, stack); return true; }; diff --git a/mobile/lib/pages/common/tab_controller.page.dart b/mobile/lib/pages/common/tab_controller.page.dart index 3d4b19cba0..1ba9650056 100644 --- a/mobile/lib/pages/common/tab_controller.page.dart +++ b/mobile/lib/pages/common/tab_controller.page.dart @@ -61,53 +61,6 @@ class TabControllerPage extends HookConsumerWidget { ref.read(tabProvider.notifier).state = TabEnum.values[index]; } - navigationRail(TabsRouter tabsRouter) { - return NavigationRail( - labelType: NavigationRailLabelType.all, - selectedIndex: tabsRouter.activeIndex, - onDestinationSelected: (index) => - onNavigationSelected(tabsRouter, index), - selectedIconTheme: IconThemeData( - color: context.primaryColor, - ), - selectedLabelTextStyle: TextStyle( - color: context.primaryColor, - ), - useIndicator: false, - destinations: [ - NavigationRailDestination( - padding: EdgeInsets.only( - top: MediaQuery.of(context).padding.top + 4, - left: 4, - right: 4, - bottom: 4, - ), - icon: const Icon(Icons.photo_library_outlined), - selectedIcon: const Icon(Icons.photo_library), - label: const Text('tab_controller_nav_photos').tr(), - ), - NavigationRailDestination( - padding: const EdgeInsets.all(4), - icon: const Icon(Icons.search_rounded), - selectedIcon: const Icon(Icons.search), - label: const Text('tab_controller_nav_search').tr(), - ), - NavigationRailDestination( - padding: const EdgeInsets.all(4), - icon: const Icon(Icons.photo_album_outlined), - selectedIcon: const Icon(Icons.photo_album), - label: const Text('albums').tr(), - ), - NavigationRailDestination( - padding: const EdgeInsets.all(4), - icon: const Icon(Icons.space_dashboard_outlined), - selectedIcon: const Icon(Icons.space_dashboard_rounded), - label: const Text('library').tr(), - ), - ], - ); - } - bottomNavigationBar(TabsRouter tabsRouter) { return NavigationBar( selectedIndex: tabsRouter.activeIndex, @@ -186,33 +139,13 @@ class TabControllerPage extends HookConsumerWidget { canPop: tabsRouter.activeIndex == 0, onPopInvokedWithResult: (didPop, _) => !didPop ? tabsRouter.setActiveIndex(0) : null, - child: LayoutBuilder( - builder: (context, constraints) { - const medium = 600; - final Widget? bottom; - final Widget body; - if (constraints.maxWidth < medium) { - // Normal phone width - bottom = bottomNavigationBar(tabsRouter); - body = child; - } else { - // Medium tablet width - bottom = null; - body = Row( - children: [ - navigationRail(tabsRouter), - Expanded(child: child), - ], - ); - } - return Scaffold( - body: HeroControllerScope( - controller: HeroController(), - child: body, - ), - bottomNavigationBar: multiselectEnabled ? null : bottom, - ); - }, + child: Scaffold( + body: HeroControllerScope( + controller: HeroController(), + child: child, + ), + bottomNavigationBar: + multiselectEnabled ? null : bottomNavigationBar(tabsRouter), ), ); }, diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index 3915ac3991..48d2c685ba 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -59,7 +59,7 @@ class LibraryPage extends ConsumerWidget { icon: Icons.link_outlined, label: 'shared_links'.tr(), ), - const SizedBox(width: 8), + SizedBox(width: trashEnabled ? 8 : 0), trashEnabled ? ActionButton( onPressed: () => context.pushRoute(const TrashRoute()), @@ -197,58 +197,65 @@ class PeopleCollectionCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final people = ref.watch(getAllPeopleProvider); - final size = MediaQuery.of(context).size.width * 0.5 - 20; - return GestureDetector( - onTap: () => context.pushRoute(const PeopleCollectionRoute()), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: size, - width: size, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: people.widgetWhen( - onData: (people) { - return GridView.count( - crossAxisCount: 2, - padding: const EdgeInsets.all(12), - crossAxisSpacing: 8, - mainAxisSpacing: 8, - physics: const NeverScrollableScrollPhysics(), - children: people.take(4).map((person) { - return CircleAvatar( - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: ApiService.getRequestHeaders(), - ), + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final widthFactor = isTablet ? 0.25 : 0.5; + final size = MediaQuery.of(context).size.width * widthFactor - 20.0; + + return GestureDetector( + onTap: () => context.pushRoute(const PeopleCollectionRoute()), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: size, + width: size, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: LinearGradient( + colors: [ + context.colorScheme.primary.withAlpha(30), + context.colorScheme.primary.withAlpha(25), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: people.widgetWhen( + onData: (people) { + return GridView.count( + crossAxisCount: 2, + padding: const EdgeInsets.all(12), + crossAxisSpacing: 8, + mainAxisSpacing: 8, + physics: const NeverScrollableScrollPhysics(), + children: people.take(4).map((person) { + return CircleAvatar( + backgroundImage: NetworkImage( + getFaceThumbnailUrl(person.id), + headers: ApiService.getRequestHeaders(), + ), + ); + }).toList(), ); - }).toList(), - ); - }, - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'people'.tr(), - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.onSurface, - fontWeight: FontWeight.w500, + }, + ), ), - ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'people'.tr(), + style: context.textTheme.titleSmall?.copyWith( + color: context.colorScheme.onSurface, + fontWeight: FontWeight.w500, + ), + ), + ), + ], ), - ], - ), + ); + }, ); } } @@ -260,55 +267,61 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final albums = ref.watch(localAlbumsProvider); - final size = MediaQuery.of(context).size.width * 0.5 - 20; + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final widthFactor = isTablet ? 0.25 : 0.5; + final size = MediaQuery.of(context).size.width * widthFactor - 20.0; - return GestureDetector( - onTap: () => context.pushRoute( - const LocalAlbumsRoute(), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: size, - width: size, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: GridView.count( - crossAxisCount: 2, - padding: const EdgeInsets.all(12), - crossAxisSpacing: 8, - mainAxisSpacing: 8, - physics: const NeverScrollableScrollPhysics(), - children: albums.take(4).map((album) { - return AlbumThumbnailCard( - album: album, - showTitle: false, - ); - }).toList(), - ), + return GestureDetector( + onTap: () => context.pushRoute( + const LocalAlbumsRoute(), ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'on_this_device'.tr(), - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.onSurface, - fontWeight: FontWeight.w500, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: size, + width: size, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: LinearGradient( + colors: [ + context.colorScheme.primary.withAlpha(30), + context.colorScheme.primary.withAlpha(25), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: GridView.count( + crossAxisCount: 2, + padding: const EdgeInsets.all(12), + crossAxisSpacing: 8, + mainAxisSpacing: 8, + physics: const NeverScrollableScrollPhysics(), + children: albums.take(4).map((album) { + return AlbumThumbnailCard( + album: album, + showTitle: false, + ); + }).toList(), + ), ), - ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'on_this_device'.tr(), + style: context.textTheme.titleSmall?.copyWith( + color: context.colorScheme.onSurface, + fontWeight: FontWeight.w500, + ), + ), + ), + ], ), - ], - ), + ); + }, ); } } @@ -317,44 +330,51 @@ class PlacesCollectionCard extends StatelessWidget { const PlacesCollectionCard({super.key}); @override Widget build(BuildContext context) { - final size = MediaQuery.of(context).size.width * 0.5 - 20; - return GestureDetector( - onTap: () => context.pushRoute(const PlacesCollectionRoute()), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: size, - width: size, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: context.colorScheme.secondaryContainer.withAlpha(100), - ), - child: IgnorePointer( - child: MapThumbnail( - zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final widthFactor = isTablet ? 0.25 : 0.5; + final size = MediaQuery.of(context).size.width * widthFactor - 20.0; + + return GestureDetector( + onTap: () => context.pushRoute(const PlacesCollectionRoute()), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: size, + width: size, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: context.colorScheme.secondaryContainer.withAlpha(100), + ), + child: IgnorePointer( + child: MapThumbnail( + zoom: 8, + centre: const LatLng( + 21.44950, + -157.91959, + ), + showAttribution: false, + themeMode: + context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, + ), ), - showAttribution: false, - themeMode: - context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'places'.tr(), - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.onSurface, - fontWeight: FontWeight.w500, + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'places'.tr(), + style: context.textTheme.titleSmall?.copyWith( + color: context.colorScheme.onSurface, + fontWeight: FontWeight.w500, + ), + ), ), - ), + ], ), - ], - ), + ); + }, ); } } diff --git a/mobile/lib/pages/library/people/people_collection.page.dart b/mobile/lib/pages/library/people/people_collection.page.dart index b3f6882808..ad78e27a41 100644 --- a/mobile/lib/pages/library/people/people_collection.page.dart +++ b/mobile/lib/pages/library/people/people_collection.page.dart @@ -29,76 +29,86 @@ class PeopleCollectionPage extends HookConsumerWidget { ); } - return Scaffold( - appBar: AppBar( - title: Text('people'.tr()), - ), - body: people.when( - data: (people) { - return GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - childAspectRatio: 0.85, - ), - padding: const EdgeInsets.symmetric(vertical: 32), - itemCount: people.length, - itemBuilder: (context, index) { - final person = people[index]; + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final isPortrait = + MediaQuery.of(context).orientation == Orientation.portrait; - return Column( - children: [ - GestureDetector( - onTap: () { - context.pushRoute( - PersonResultRoute( - personId: person.id, - personName: person.name, - ), - ); - }, - child: Material( - shape: const CircleBorder(side: BorderSide.none), - elevation: 3, - child: CircleAvatar( - maxRadius: 96 / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), - ), - ), - ), - const SizedBox(height: 12), - GestureDetector( - onTap: () => showNameEditModel(person.id, person.name), - child: person.name.isEmpty - ? Text( - 'add_a_name'.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - color: context.colorScheme.primary, + return Scaffold( + appBar: AppBar( + title: Text('people'.tr()), + ), + body: people.when( + data: (people) { + return GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isTablet ? 6 : 3, + childAspectRatio: 0.85, + mainAxisSpacing: isPortrait && isTablet ? 36 : 0, + ), + padding: const EdgeInsets.symmetric(vertical: 32), + itemCount: people.length, + itemBuilder: (context, index) { + final person = people[index]; + + return Column( + children: [ + GestureDetector( + onTap: () { + context.pushRoute( + PersonResultRoute( + personId: person.id, + personName: person.name, ), - ) - : Padding( - padding: - const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - person.name, - overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + ); + }, + child: Material( + shape: const CircleBorder(side: BorderSide.none), + elevation: 3, + child: CircleAvatar( + maxRadius: isTablet ? 120 / 2 : 96 / 2, + backgroundImage: NetworkImage( + getFaceThumbnailUrl(person.id), + headers: headers, ), ), - ), - ], + ), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: () => showNameEditModel(person.id, person.name), + child: person.name.isEmpty + ? Text( + 'add_a_name'.tr(), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.primary, + ), + ) + : Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: Text( + person.name, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ], + ); + }, ); }, - ); - }, - error: (error, stack) => const Text("error"), - loading: () => const CircularProgressIndicator(), - ), + error: (error, stack) => const Text("error"), + loading: () => const CircularProgressIndicator(), + ), + ); + }, ); } } diff --git a/mobile/lib/providers/asset_viewer/download.provider.dart b/mobile/lib/providers/asset_viewer/download.provider.dart index d4aa2823b5..68b120c38a 100644 --- a/mobile/lib/providers/asset_viewer/download.provider.dart +++ b/mobile/lib/providers/asset_viewer/download.provider.dart @@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/download/download_state.model.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; +import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/download.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/services/share.service.dart'; @@ -15,10 +16,12 @@ import 'package:immich_mobile/widgets/common/share_dialog.dart'; class DownloadStateNotifier extends StateNotifier { final DownloadService _downloadService; final ShareService _shareService; + final AlbumService _albumService; DownloadStateNotifier( this._downloadService, this._shareService, + this._albumService, ) : super( DownloadState( downloadStatus: TaskStatus.complete, @@ -76,7 +79,7 @@ class DownloadStateNotifier extends StateNotifier { switch (update.status) { case TaskStatus.complete: - _downloadService.saveImage(update.task); + _downloadService.saveImageWithPath(update.task); _onDownloadComplete(update.task.taskId); break; @@ -133,6 +136,7 @@ class DownloadStateNotifier extends StateNotifier { showProgress: false, ); } + _albumService.refreshDeviceAlbums(); }); } @@ -187,5 +191,6 @@ final downloadStateProvider = ((ref) => DownloadStateNotifier( ref.watch(downloadServiceProvider), ref.watch(shareServiceProvider), + ref.watch(albumServiceProvider), )), ); diff --git a/mobile/lib/repositories/file_media.repository.dart b/mobile/lib/repositories/file_media.repository.dart index 5612b378c3..15f7a51e15 100644 --- a/mobile/lib/repositories/file_media.repository.dart +++ b/mobile/lib/repositories/file_media.repository.dart @@ -25,6 +25,20 @@ class FileMediaRepository implements IFileMediaRepository { return AssetMediaRepository.toAsset(entity); } + @override + Future saveImageWithFile( + String filePath, { + String? title, + String? relativePath, + }) async { + final entity = await PhotoManager.editor.saveImageWithPath( + filePath, + title: title, + relativePath: relativePath, + ); + return AssetMediaRepository.toAsset(entity); + } + @override Future saveLivePhoto({ required File image, diff --git a/mobile/lib/services/download.service.dart b/mobile/lib/services/download.service.dart index 996cbe61f1..7cf6f309e9 100644 --- a/mobile/lib/services/download.service.dart +++ b/mobile/lib/services/download.service.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -12,6 +12,7 @@ import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/utils/download.dart'; +import 'package:logging/logging.dart'; final downloadServiceProvider = Provider( (ref) => DownloadService( @@ -23,6 +24,7 @@ final downloadServiceProvider = Provider( class DownloadService { final IDownloadRepository _downloadRepository; final IFileMediaRepository _fileMediaRepository; + final Logger _log = Logger("DownloadService"); void Function(TaskStatusUpdate)? onImageDownloadStatus; void Function(TaskStatusUpdate)? onVideoDownloadStatus; void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus; @@ -55,19 +57,25 @@ class DownloadService { onLivePhotoDownloadStatus?.call(update); } - Future saveImage(Task task) async { + Future saveImageWithPath(Task task) async { final filePath = await task.filePath(); final title = task.filename; final relativePath = Platform.isAndroid ? 'DCIM/Immich' : null; - final data = await File(filePath).readAsBytes(); - - final Asset? resultAsset = await _fileMediaRepository.saveImage( - data, - title: title, - relativePath: relativePath, - ); - - return resultAsset != null; + try { + final Asset? resultAsset = await _fileMediaRepository.saveImageWithFile( + filePath, + title: title, + relativePath: relativePath, + ); + return resultAsset != null; + } catch (error, stack) { + _log.severe("Error saving image", error, stack); + return false; + } finally { + if (await File(filePath).exists()) { + await File(filePath).delete(); + } + } } Future saveVideo(Task task) async { @@ -75,58 +83,74 @@ class DownloadService { final title = task.filename; final relativePath = Platform.isAndroid ? 'DCIM/Immich' : null; final file = File(filePath); - - final Asset? resultAsset = await _fileMediaRepository.saveVideo( - file, - title: title, - relativePath: relativePath, - ); - - return resultAsset != null; + try { + final Asset? resultAsset = await _fileMediaRepository.saveVideo( + file, + title: title, + relativePath: relativePath, + ); + return resultAsset != null; + } catch (error, stack) { + _log.severe("Error saving video", error, stack); + return false; + } finally { + if (await file.exists()) { + await file.delete(); + } + } } Future saveLivePhotos( Task task, String livePhotosId, ) async { + final records = await _downloadRepository.getLiveVideoTasks(); + if (records.length < 2) { + return false; + } + + final imageRecord = + _findTaskRecord(records, livePhotosId, LivePhotosPart.image); + final videoRecord = + _findTaskRecord(records, livePhotosId, LivePhotosPart.video); + final imageFilePath = await imageRecord.task.filePath(); + final videoFilePath = await videoRecord.task.filePath(); + try { - final records = await _downloadRepository.getLiveVideoTasks(); - if (records.length < 2) { - return false; - } - - final imageRecord = records.firstWhere( - (record) { - final metadata = LivePhotosMetadata.fromJson(record.task.metaData); - return metadata.id == livePhotosId && - metadata.part == LivePhotosPart.image; - }, - ); - - final videoRecord = records.firstWhere((record) { - final metadata = LivePhotosMetadata.fromJson(record.task.metaData); - return metadata.id == livePhotosId && - metadata.part == LivePhotosPart.video; - }); - - final imageFilePath = await imageRecord.task.filePath(); - final videoFilePath = await videoRecord.task.filePath(); - - final resultAsset = await _fileMediaRepository.saveLivePhoto( + final result = await _fileMediaRepository.saveLivePhoto( image: File(imageFilePath), video: File(videoFilePath), title: task.filename, ); + return result != null; + } on PlatformException catch (error, stack) { + // Handle saving MotionPhotos on iOS + if (error.code == 'PHPhotosErrorDomain (-1)') { + final result = await _fileMediaRepository + .saveImageWithFile(imageFilePath, title: task.filename); + return result != null; + } + _log.severe("Error saving live photo", error, stack); + return false; + } catch (error, stack) { + _log.severe("Error saving live photo", error, stack); + return false; + } finally { + final imageFile = File(imageFilePath); + if (await imageFile.exists()) { + await imageFile.delete(); + } + + final videoFile = File(videoFilePath); + if (await videoFile.exists()) { + await videoFile.delete(); + } + await _downloadRepository.deleteRecordsWithIds([ imageRecord.task.taskId, videoRecord.task.taskId, ]); - - return resultAsset != null; - } catch (error) { - debugPrint("Error saving live photo: $error"); - return false; } } @@ -151,7 +175,9 @@ class DownloadService { await _downloadRepository.download( _buildDownloadTask( asset.livePhotoVideoId!, - asset.fileName.toUpperCase().replaceAll(".HEIC", '.MOV'), + asset.fileName + .toUpperCase() + .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), group: downloadGroupLivePhoto, metadata: LivePhotosMetadata( part: LivePhotosPart.video, @@ -191,3 +217,14 @@ class DownloadService { ); } } + +TaskRecord _findTaskRecord( + List records, + String livePhotosId, + LivePhotosPart part, +) { + return records.firstWhere((record) { + final metadata = LivePhotosMetadata.fromJson(record.task.metaData); + return metadata.id == livePhotosId && metadata.part == part; + }); +} diff --git a/mobile/lib/widgets/asset_grid/thumbnail_image.dart b/mobile/lib/widgets/asset_grid/thumbnail_image.dart index 6cadef763d..40c6f219b7 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_image.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_image.dart @@ -138,10 +138,31 @@ class ThumbnailImage extends ConsumerWidget { tag: isFromDto ? '${asset.remoteId}-$heroOffset' : asset.id + heroOffset, - child: ImmichThumbnail( - asset: asset, - height: 250, - width: 250, + child: Stack( + children: [ + ImmichThumbnail( + asset: asset, + height: 250, + width: 250, + ), + Container( + height: 250, + width: 250, + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [ + Color.fromRGBO(0, 0, 0, 0.1), + Colors.transparent, + Colors.transparent, + Color.fromRGBO(0, 0, 0, 0.1), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: [0, 0.3, 0.6, 1], + ), + ), + ), + ], ), ), ); @@ -153,11 +174,8 @@ class ThumbnailImage extends ConsumerWidget { color: canDeselect ? assetContainerColor : Colors.grey, ), child: ClipRRect( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(15.0), - bottomRight: Radius.circular(15.0), - bottomLeft: Radius.circular(15.0), - topLeft: Radius.zero, + borderRadius: const BorderRadius.all( + Radius.circular(15.0), ), child: image, ), @@ -177,7 +195,33 @@ class ThumbnailImage extends ConsumerWidget { ) : const Border(), ), - child: buildImage(), + child: Stack( + children: [ + buildImage(), + if (showStorageIndicator) + Positioned( + right: 8, + bottom: 5, + child: Icon( + storageIcon(asset), + color: Colors.white.withOpacity(.8), + size: 16, + ), + ), + if (asset.isFavorite) + const Positioned( + left: 8, + bottom: 5, + child: Icon( + Icons.favorite, + color: Colors.white, + size: 16, + ), + ), + if (!asset.isImage) buildVideoIcon(), + if (asset.stackCount > 0) buildStackIcon(), + ], + ), ), if (multiselectEnabled) Padding( @@ -187,28 +231,6 @@ class ThumbnailImage extends ConsumerWidget { child: buildSelectionIcon(asset), ), ), - if (showStorageIndicator) - Positioned( - right: 8, - bottom: 5, - child: Icon( - storageIcon(asset), - color: Colors.white.withOpacity(.8), - size: 16, - ), - ), - if (asset.isFavorite) - const Positioned( - left: 8, - bottom: 5, - child: Icon( - Icons.favorite, - color: Colors.white, - size: 18, - ), - ), - if (!asset.isImage) buildVideoIcon(), - if (asset.stackCount > 0) buildStackIcon(), ], ); } diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index 46e8671858..51383fe195 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -176,7 +176,7 @@ class LoginForm extends HookConsumerWidget { populateTestLoginInfo1() { usernameController.text = 'testuser@email.com'; passwordController.text = 'password'; - serverEndpointController.text = 'http://192.168.1.118:2283/api'; + serverEndpointController.text = 'http://10.1.15.216:2283/api'; } login() async { diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 1f29bf7830..fe40017ba8 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1211,18 +1211,18 @@ packages: dependency: "direct main" description: name: photo_manager - sha256: "32a1ce1095aeaaa792a29f28c1f74613aa75109f21c2d4ab85be3ad9964230a4" + sha256: "70159eee32203e8162d49d588232f0299ed3f383c63eef1e899cb6b83dee6b26" url: "https://pub.dev" source: hosted - version: "3.5.0" + version: "3.5.1" photo_manager_image_provider: dependency: "direct main" description: name: photo_manager_image_provider - sha256: "38ef1023dc11de3a8669f16e7c981673b3c5cfee715d17120f4b87daa2cdd0af" + sha256: b6015b67b32f345f57cf32c126f871bced2501236c405aafaefa885f7c821e4f url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.2.0" platform: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 703736e3d9..5177e64bcf 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: sdk: flutter path_provider_ios: - photo_manager: ^3.5.0 - photo_manager_image_provider: ^2.1.1 + photo_manager: ^3.5.1 + photo_manager_image_provider: ^2.2.0 flutter_hooks: ^0.20.4 hooks_riverpod: ^2.4.9 riverpod_annotation: ^2.3.3 diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 1e900f7cb9..c1e68d967f 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -12,7 +12,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "typescript": "^5.3.3" } }, @@ -22,9 +22,9 @@ "integrity": "sha512-8tKiYffhwTGHSHYGnZ3oneLGCjX0po/XAXQ5Ng9fqKkvIdl/xz8+Vh8i+6xjzZqvZ2pLVpUcuSfnvNI/x67L0g==" }, "node_modules/@types/node": { - "version": "20.16.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", - "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "version": "20.16.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.13.tgz", + "integrity": "sha512-GjQ7im10B0labo8ZGXDGROUl9k0BNyDgzfGpb4g/cl+4yYDWVKcozANF4FGr4/p0O/rAkQClM6Wiwkije++1Tg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index d0292a1b7c..e323ea15c5 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "typescript": "^5.3.3" }, "repository": { diff --git a/readme_i18n/README_ar_JO.md b/readme_i18n/README_ar_JO.md index 7df39d226b..8fa4ac1195 100644 --- a/readme_i18n/README_ar_JO.md +++ b/readme_i18n/README_ar_JO.md @@ -32,6 +32,7 @@ Русский Português Brasileiro Svenska + ภาษาไทย

## تنصل diff --git a/readme_i18n/README_ca_ES.md b/readme_i18n/README_ca_ES.md index ed14649e0a..66a8b584fd 100644 --- a/readme_i18n/README_ca_ES.md +++ b/readme_i18n/README_ca_ES.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## Avís legal diff --git a/readme_i18n/README_de_DE.md b/readme_i18n/README_de_DE.md index 7a59e3444e..d6c69106f3 100644 --- a/readme_i18n/README_de_DE.md +++ b/readme_i18n/README_de_DE.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## Warnung diff --git a/readme_i18n/README_es_ES.md b/readme_i18n/README_es_ES.md index 726a504526..0b0dbf919d 100644 --- a/readme_i18n/README_es_ES.md +++ b/readme_i18n/README_es_ES.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## Advertencia diff --git a/readme_i18n/README_fr_FR.md b/readme_i18n/README_fr_FR.md index da52fe28a6..e2f979d254 100644 --- a/readme_i18n/README_fr_FR.md +++ b/readme_i18n/README_fr_FR.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## Clause de non-responsabilité diff --git a/readme_i18n/README_it_IT.md b/readme_i18n/README_it_IT.md index 1523143f06..7208df7e24 100644 --- a/readme_i18n/README_it_IT.md +++ b/readme_i18n/README_it_IT.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## Declino di responsabilità diff --git a/readme_i18n/README_ja_JP.md b/readme_i18n/README_ja_JP.md index 98ff8e68d9..828afa9812 100644 --- a/readme_i18n/README_ja_JP.md +++ b/readme_i18n/README_ja_JP.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## 免責事項 diff --git a/readme_i18n/README_ko_KR.md b/readme_i18n/README_ko_KR.md index 66df040d75..8b280e0a9b 100644 --- a/readme_i18n/README_ko_KR.md +++ b/readme_i18n/README_ko_KR.md @@ -33,6 +33,7 @@ Português Brasileiro Svenska العربية +ภาษาไทย

diff --git a/readme_i18n/README_nl_NL.md b/readme_i18n/README_nl_NL.md index 1c877d9d3e..e1cf6d66f5 100644 --- a/readme_i18n/README_nl_NL.md +++ b/readme_i18n/README_nl_NL.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## Disclaimer diff --git a/readme_i18n/README_pt_BR.md b/readme_i18n/README_pt_BR.md index 51ea8238da..5468ebb4c4 100644 --- a/readme_i18n/README_pt_BR.md +++ b/readme_i18n/README_pt_BR.md @@ -34,6 +34,7 @@ Svenska العربية Tiếng Việt +ภาษาไทย

diff --git a/readme_i18n/README_ru_RU.md b/readme_i18n/README_ru_RU.md index 11a2a34f33..0ff3e3f08f 100644 --- a/readme_i18n/README_ru_RU.md +++ b/readme_i18n/README_ru_RU.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## Предупреждение diff --git a/readme_i18n/README_sv_SE.md b/readme_i18n/README_sv_SE.md index 3673eab57c..29706acb55 100644 --- a/readme_i18n/README_sv_SE.md +++ b/readme_i18n/README_sv_SE.md @@ -33,6 +33,7 @@ Русский Português Brasileiro العربية + ภาษาไทย

## Ansvarsfriskrivning diff --git a/readme_i18n/README_th_TH.md b/readme_i18n/README_th_TH.md new file mode 100644 index 0000000000..6a6b70d435 --- /dev/null +++ b/readme_i18n/README_th_TH.md @@ -0,0 +1,134 @@ +

+
+ License: AGPLv3 + + Discord + +
+
+

+ +

+ +

+ +

โซลูชันการจัดการภาพถ่ายและวิดีโอแบบโฮสต์เองที่มีประสิทธิภาพสูง

+
+ + + + +
+ +

+ English + Català + Español + Français + Italiano + 日本語 + 한국어 + Deutsch + Nederlands + Türkçe + 中文 + Русский + Português Brasileiro + Svenska + العربية + Tiếng Việt +

+ +## ข้อจำกัดความรับผิดชอบ + +- ⚠️ โพรเจกต์นี้กำลังอยู่ระหว่างการพัฒนา**ที่มีการเปลี่ยนแปลงบ่อยมาก** +- ⚠️ คาดว่าจะมีข้อผิดพลาดและการเปลี่ยนแปลงที่ส่งผลเสีย +- ⚠️ **ห้ามใช้แอปนี้เป็นวิธีการเดียวในการจัดเก็บภาพถ่ายและวิดีโอของคุณ** +- ⚠️ ปฏิบัติตามแผนการสำรองข้อมูลแบบ [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) สำหรับภาพถ่ายและวิดีโอที่สำคัญของคุณอยู่เสมอ! + +> [!NOTE] +> คุณสามารถหาคู่มือหลัก รวมถึงคู่มือการติดตั้ง ได้ที่ https://immich.app/ + +## ลิงก์ + +- [คู่มือ](https://immich.app/docs) +- [เกี่ยวกับ](https://immich.app/docs/overview/introduction) +- [การติดตั้ง](https://immich.app/docs/install/requirements) +- [โรดแมป](https://immich.app/roadmap) +- [สาธิต](#สาธิต) +- [คุณสมบัติ](#คุณสมบัติ) +- [การแปลภาษา](https://immich.app/docs/developer/translations) +- [สนับสนุนโพรเจกต์](https://immich.app/docs/overview/support-the-project) + +## สาธิต + +เข้าถึงการสาธิตได้ [ที่นี่](https://demo.immich.app) โดยการสาธิตนี้ทำงานบน Oracle VM Free-tier ตั้งอยู่ที่อัมสเตอร์ดัม ใช้ซีพียู ARM64 quad-core 2.4Ghz และแรม 24GB + +สำหรับแอปมือถือ คุณสามารถใช้ `https://demo.immich.app/api` เป็น `Server Endpoint URL` + +### ข้อมูลการเข้าสู่ระบบ + +| อีเมล | รหัสผ่าน | +| --------------- | -------- | +| demo@immich.app | demo | + +## คุณสมบัติ + +| คุณสมบัติ | มือถือ | เว็บ | +| :----------------------------------------- | ------ | ------ | +| อัปโหลดและดูวิดีโอและภาพถ่าย | ใช่ | ใช่ | +| การสำรองข้อมูลอัตโนมัติเมื่อเปิดแอป | ใช่ | N/A | +| ป้องกันการซ้ำซ้อนของไฟล์ | ใช่ | ใช่ | +| เลือกอัลบั้มสำหรับสำรองข้อมูล | ใช่ | N/A | +| ดาวน์โหลดภาพถ่ายและวิดีโอไปยังอุปกรณ์ | ใช่ | ใช่ | +| รองรับผู้ใช้หลายคน | ใช่ | ใช่ | +| อัลบั้มและอัลบั้มแชร์ | ใช่ | ใช่ | +| แถบเลื่อนแบบลากได้ | ใช่ | ใช่ | +| รองรับรูปแบบไฟล์ RAW | ใช่ | ใช่ | +| ดูข้อมูลเมตา (EXIF, แผนที่) | ใช่ | ใช่ | +| ค้นหาจากข้อมูลเมตา วัตถุ ใบหน้า และ CLIP | ใช่ | ใช่ | +| ฟังก์ชันการจัดการผู้ดูแลระบบ | ไม่ใช่ | ใช่ | +| การสำรองข้อมูลพื้นหลัง | ใช่ | N/A | +| การเลื่อนแบบเสมือน | ใช่ | ใช่ | +| รองรับ OAuth | ใช่ | ใช่ | +| คีย์ API | N/A | ใช่ | +| การสำรองและเล่น LivePhoto/MotionPhoto | ใช่ | ใช่ | +| รองรับการแสดงภาพ 360 องศา | ไม่ใช่ | ใช่ | +| โครงสร้างการจัดเก็บข้อมูลที่ผู้ใช้กำหนดเอง | ใช่ | ใช่ | +| การแชร์สาธารณะ | ใช่ | ใช่ | +| การจัดเก็บและรายการโปรด | ใช่ | ใช่ | +| แผนที่ทั่วโลก | ใช่ | ใช่ | +| การแชร์กับคู่หู | ใช่ | ใช่ | +| การจดจำใบหน้าและการจัดกลุ่ม | ใช่ | ใช่ | +| ความทรงจำ (x ปีที่แล้ว) | ใช่ | ใช่ | +| รองรับแบบออฟไลน์ | ใช่ | ไม่ใช่ | +| แกลเลอรีแบบอ่านอย่างเดียว | ใช่ | ใช่ | +| ภาพถ่ายซ้อนกัน | ใช่ | ใช่ | + +## การแปลภาษา + +อ่านเพิ่มเติมเกี่ยวกับการแปลภาษา [ที่นี่](https://immich.app/docs/developer/translations) + + + สถานะการแปล + + +## กิจกรรมของคลังเก็บข้อมูล + +![กิจกรรม](https://repobeats.axiom.co/api/embed/9e86d9dc3ddd137161f2f6d2e758d7863b1789cb.svg "ภาพการวิเคราะห์ของ Repobeats") + +## ประวัติการให้ดาว + + + + + + แผนภูมิประวัติการให้ดาว + + + +## ผู้ร่วมพัฒนา + + + + diff --git a/readme_i18n/README_tr_TR.md b/readme_i18n/README_tr_TR.md index f95d914880..6bf23be5f8 100644 --- a/readme_i18n/README_tr_TR.md +++ b/readme_i18n/README_tr_TR.md @@ -32,6 +32,7 @@ Português Brasileiro Svenska العربية + ภาษาไทย

## Feragatname diff --git a/readme_i18n/README_vi_VN.md b/readme_i18n/README_vi_VN.md index 7ec4b9c948..69d7a151be 100644 --- a/readme_i18n/README_vi_VN.md +++ b/readme_i18n/README_vi_VN.md @@ -35,6 +35,7 @@ Svenska العربية Tiếng Việt +ภาษาไทย

diff --git a/readme_i18n/README_zh_CN.md b/readme_i18n/README_zh_CN.md index 6355cd65ed..380dc25992 100644 --- a/readme_i18n/README_zh_CN.md +++ b/readme_i18n/README_zh_CN.md @@ -36,7 +36,8 @@ Português Brasileiro Svenska العربية - + ภาษาไทย +

## 免责声明 diff --git a/server/Dockerfile b/server/Dockerfile index 5b92311dc9..5d89b64570 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,5 +1,5 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:20241016@sha256:00ea9ef1c9aed4499353d5cccbd63f52d125c18e264aac54d1139b6da6715a62 AS dev +FROM ghcr.io/immich-app/base-server-dev:20241022@sha256:22941f8bd36e27a2a659e755ce8ee3e3906adfa41a3ad15e81cad0ed333c14ff AS dev RUN apt-get install --no-install-recommends -yqq tini WORKDIR /usr/src/app @@ -37,11 +37,12 @@ WORKDIR /usr/src/app COPY web/package*.json web/svelte.config.js ./ RUN npm ci COPY web ./ +COPY i18n ../i18n RUN npm run build # prod build -FROM ghcr.io/immich-app/base-server-prod:20241016@sha256:063563dc3a1d9e7a105b1847f93eb82a8dad808b03e7acae8e7c5007fb732cee +FROM ghcr.io/immich-app/base-server-prod:20241022@sha256:6676a716a11106887c98a2d4ac4677a92c6f80ba6da3e496de1b302a56882ef5 WORKDIR /usr/src/app ENV NODE_ENV=production \ diff --git a/server/package-lock.json b/server/package-lock.json index 7830c22a3b..8f098542a5 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -20,7 +20,7 @@ "@nestjs/swagger": "^7.1.8", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.2", - "@opentelemetry/auto-instrumentations-node": "^0.50.0", + "@opentelemetry/auto-instrumentations-node": "^0.51.0", "@opentelemetry/context-async-hooks": "^1.24.0", "@opentelemetry/exporter-prometheus": "^0.53.0", "@opentelemetry/sdk-node": "^0.53.0", @@ -83,7 +83,7 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^3.0.0", "@types/pngjs": "^6.0.5", @@ -2076,9 +2076,9 @@ } }, "node_modules/@nestjs/common": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.4.tgz", - "integrity": "sha512-0j2/zqRw9nvHV1GKTktER8B/hIC/Z8CYFjN/ZqUuvwayCH+jZZBhCR2oRyuvLTXdnlSmtCAg2xvQ0ULqQvzqhA==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.5.tgz", + "integrity": "sha512-N/yUyuYCBMb0+H6jHhntR7PURzji0usID/DByhOfooyk/aPGscI0aQKwOA6edlJlT92hHUvXYLJ5p3npj7KcjQ==", "dependencies": { "iterare": "1.2.1", "tslib": "2.7.0", @@ -2123,9 +2123,9 @@ } }, "node_modules/@nestjs/core": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.4.tgz", - "integrity": "sha512-y9tjmAzU6LTh1cC/lWrRsCcOd80khSR0qAHAqwY2svbW+AhsR/XCzgpZrAAKJrm/dDfjLCZKyxJSayeirGcW5Q==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.5.tgz", + "integrity": "sha512-wk0KJ+6tuidqAdeemsQ40BCp1BgMsSuSLG577aqXLxXYoa8FQYPrdxoSzd05znYLwJYM55fisZWb3FLF9HT2qw==", "hasInstallScript": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", @@ -2196,13 +2196,13 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.4.tgz", - "integrity": "sha512-y52q1MxhbHaT3vAgWd08RgiYon0lJgtTa8U6g6gV0KI0IygwZhDQFJVxnrRDUdxQGIP5CKHmfQu3sk9gTNFoEA==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.5.tgz", + "integrity": "sha512-a629r8R8KC4skhdieQ0aIWH5vDBUFntWnWKFyDXQrll6/CllSchfWm87mWF39seaW6bXYtQtAEZY66JrngdrGA==", "dependencies": { "body-parser": "1.20.3", "cors": "2.8.5", - "express": "4.21.0", + "express": "4.21.1", "multer": "1.4.4-lts.1", "tslib": "2.7.0" }, @@ -2221,9 +2221,9 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@nestjs/platform-socket.io": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.4.tgz", - "integrity": "sha512-5GEYUA3sNbX2jOBP6FmrIK/zv9VCdvpdr4Sef1OKvt1U0qsV1YgmWPWDPumZM77n5DI0VHSJPyo7yjZaEKWOiQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.5.tgz", + "integrity": "sha512-dHkHJQArhrpkX6qBdTW2ghuja3i3cCslwy4QHY6d46u+9UyANQlsNK9wt/lZnmXfCMaci8xAJvUpyODa6YtV7g==", "dependencies": { "socket.io": "4.7.5", "tslib": "2.7.0" @@ -2269,14 +2269,14 @@ } }, "node_modules/@nestjs/schematics": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.4.tgz", - "integrity": "sha512-QpY8ez9cTvXXPr3/KBrtSgXQHMSV6BkOUYy2c2TTe6cBqriEdGnCYqGl8cnfrQl3632q3lveQPaZ/c127dHsEw==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.2.tgz", + "integrity": "sha512-D4pJ46E8llCA7WPr3cV6sfRqDlvnTjQWnF1fLyKYD3Ldl+KPtlLyIcxaqlLTB0YR9ItKNKIZTJzUehRxR7UUsQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", - "@angular-devkit/schematics": "17.3.8", - "comment-json": "4.2.3", + "@angular-devkit/core": "17.3.10", + "@angular-devkit/schematics": "17.3.10", + "comment-json": "4.2.5", "jsonc-parser": "3.3.1", "pluralize": "8.0.0" }, @@ -2284,12 +2284,81 @@ "typescript": ">=4.8.2" } }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { + "version": "17.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.10.tgz", + "integrity": "sha512-czdl54yxU5DOAGy/uUPNjJruoBDTgwi/V+eOgLNybYhgrc+TsY0f7uJ11yEk/pz5sCov7xIiS7RdRv96waS7vg==", + "dev": true, + "dependencies": { + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core/node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { + "version": "17.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.10.tgz", + "integrity": "sha512-FHcNa1ktYRd0SKExCsNJpR75RffsyuPIV8kvBXzXnLHmXMqvl25G2te3yYJ9yYqy9OLy/58HZznZTxWRyUdHOg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "17.3.10", + "jsonc-parser": "3.2.1", + "magic-string": "0.30.8", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics/node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, "node_modules/@nestjs/schematics/node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true }, + "node_modules/@nestjs/schematics/node_modules/picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/swagger": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.4.2.tgz", @@ -2323,9 +2392,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.4.tgz", - "integrity": "sha512-qRGFj51A5RM7JqA8pcyEwSLA3Y0dle/PAZ8oxP0suimoCusRY3Tk7wYqutZdCNj1ATb678SDaUZDHk2pwSv9/g==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.5.tgz", + "integrity": "sha512-3NhmztE+fK3MuuOZhXihvMIhxm0QuDM2BneHvM5A0oVLG+STsAeGBqbDr/Ef2qsvqH5HaqvfGbVJ4N1DQnZE5A==", "dev": true, "dependencies": { "tslib": "2.7.0" @@ -2371,9 +2440,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.4.tgz", - "integrity": "sha512-ZHnak04i/iKBS0csjJa7K6D6xdsB0Yz6duJuCR7xGLItchFK+Ne21m9rEF8ffvW74U7UAYkQHBgD5242LBBYiQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.5.tgz", + "integrity": "sha512-LbL/HRLWQUBTUPY7swojOHdvokyVGINIiuP/VmRdhob4T751r+9i09z2RqRpP71psuom9mnRHYI1+vT2FABrAw==", "dependencies": { "iterare": "1.2.1", "object-hash": "3.0.0", @@ -2611,9 +2680,9 @@ } }, "node_modules/@opentelemetry/auto-instrumentations-node": { - "version": "0.50.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.50.2.tgz", - "integrity": "sha512-l1JWvNp5gt5Fze8X68+zjzBqiviB5B8zeepsbfpFgdDxoCVjmixg8gcMt/AmqI9Qntw2qaeXah84V14fCbVuMg==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.51.0.tgz", + "integrity": "sha512-xsgydgtJiToxvFsDcmLDrHiFfHOmdomqk4KCnr40YZdsfw7KO4RJEU0om2f7pFh6WUI5q8nSQ53QgZ+DAz6TzA==", "dependencies": { "@opentelemetry/instrumentation": "^0.53.0", "@opentelemetry/instrumentation-amqplib": "^0.42.0", @@ -2645,7 +2714,7 @@ "@opentelemetry/instrumentation-mysql2": "^0.41.0", "@opentelemetry/instrumentation-nestjs-core": "^0.40.0", "@opentelemetry/instrumentation-net": "^0.39.0", - "@opentelemetry/instrumentation-pg": "^0.45.1", + "@opentelemetry/instrumentation-pg": "^0.46.0", "@opentelemetry/instrumentation-pino": "^0.42.0", "@opentelemetry/instrumentation-redis": "^0.42.0", "@opentelemetry/instrumentation-redis-4": "^0.42.1", @@ -2658,7 +2727,7 @@ "@opentelemetry/resource-detector-alibaba-cloud": "^0.29.3", "@opentelemetry/resource-detector-aws": "^1.6.2", "@opentelemetry/resource-detector-azure": "^0.2.11", - "@opentelemetry/resource-detector-container": "^0.4.3", + "@opentelemetry/resource-detector-container": "^0.4.4", "@opentelemetry/resource-detector-gcp": "^0.29.12", "@opentelemetry/resources": "^1.24.0", "@opentelemetry/sdk-node": "^0.53.0" @@ -3592,9 +3661,9 @@ } }, "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.45.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.45.1.tgz", - "integrity": "sha512-GHUvPv7CQEK3RKHH3YAj6mjgJ3nZb6wRQS+t0yaRgKZzX2ggGsLN6OhRT04+IjqmMg9aIRUy1CzqwzgqAxjYbw==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.46.0.tgz", + "integrity": "sha512-PLbYYC7EIoigh9uzhBrDjyL4yhH9akjV2Mln3ci9+lD7p9HE5nUUgYCgcUasyr4bz99c8xy9ErzKLt38Y7Kodg==", "dependencies": { "@opentelemetry/core": "^1.26.0", "@opentelemetry/instrumentation": "^0.53.0", @@ -3842,9 +3911,9 @@ } }, "node_modules/@opentelemetry/resource-detector-container": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.4.3.tgz", - "integrity": "sha512-trgLpifOb3rk1l5ygqLlTFoEu7O//q4qvV0Kd5p1D/+WaEDMrp5c6ktJRGrUaU+eBQmlEjwr9BdV6SRnD2lvsA==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.4.4.tgz", + "integrity": "sha512-ZEN2mq7lIjQWJ8NTt1umtr6oT/Kb89856BOmESLSvgSHbIwOFYs7cSfSRH5bfiVw6dXTQAVbZA/wLgCHKrebJA==", "dependencies": { "@opentelemetry/core": "^1.26.0", "@opentelemetry/resources": "^1.10.0", @@ -4882,9 +4951,9 @@ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, "node_modules/@swc/core": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.35.tgz", - "integrity": "sha512-3cUteCTbr2r5jqfgx0r091sfq5Mgh6F1SQh8XAOnSvtKzwv2bC31mvBHVAieD1uPa2kHJhLav20DQgXOhpEitw==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.36.tgz", + "integrity": "sha512-bu7ymMX+LCJOSSrKank25Jaq66ymLVA9fOUuy4ck3/6rbXdLw+pIJPnIDKQ9uNcxww8KDxOuJk9Ui9pqR+aGFw==", "devOptional": true, "hasInstallScript": true, "dependencies": { @@ -4899,16 +4968,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.7.35", - "@swc/core-darwin-x64": "1.7.35", - "@swc/core-linux-arm-gnueabihf": "1.7.35", - "@swc/core-linux-arm64-gnu": "1.7.35", - "@swc/core-linux-arm64-musl": "1.7.35", - "@swc/core-linux-x64-gnu": "1.7.35", - "@swc/core-linux-x64-musl": "1.7.35", - "@swc/core-win32-arm64-msvc": "1.7.35", - "@swc/core-win32-ia32-msvc": "1.7.35", - "@swc/core-win32-x64-msvc": "1.7.35" + "@swc/core-darwin-arm64": "1.7.36", + "@swc/core-darwin-x64": "1.7.36", + "@swc/core-linux-arm-gnueabihf": "1.7.36", + "@swc/core-linux-arm64-gnu": "1.7.36", + "@swc/core-linux-arm64-musl": "1.7.36", + "@swc/core-linux-x64-gnu": "1.7.36", + "@swc/core-linux-x64-musl": "1.7.36", + "@swc/core-win32-arm64-msvc": "1.7.36", + "@swc/core-win32-ia32-msvc": "1.7.36", + "@swc/core-win32-x64-msvc": "1.7.36" }, "peerDependencies": { "@swc/helpers": "*" @@ -4920,9 +4989,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.35.tgz", - "integrity": "sha512-BQSSozVxjxS+SVQz6e3GC/+OBWGIK3jfe52pWdANmycdjF3ch7lrCKTHTU7eHwyoJ96mofszPf5AsiVJF34Fwg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.36.tgz", + "integrity": "sha512-8vDczXzCgv3ceTPhEivlpGprN44YlrCK1nbfU9g2TrhV/Aiqi09W/eM5zLesdoM1Z3mJl492gc/8nlTkpDdusw==", "cpu": [ "arm64" ], @@ -4936,9 +5005,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.35.tgz", - "integrity": "sha512-44TYdKN/EWtkU88foXR7IGki9JzhEJzaFOoPevfi9Xe7hjAD/x2+AJOWWqQNzDPMz9+QewLdUVLyR6s5okRgtg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.36.tgz", + "integrity": "sha512-Pa2Gao7+Wf5m3SsK4abKRtd48AtoUnJInvaC3d077swBfgZjbjUbQvcpdc2dOeQtWwo49rFqUZJonMsL0jnPgQ==", "cpu": [ "x64" ], @@ -4952,9 +5021,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.35.tgz", - "integrity": "sha512-ccfA5h3zxwioD+/z/AmYtkwtKz9m4rWTV7RoHq6Jfsb0cXHrd6tbcvgqRWXra1kASlE+cDWsMtEZygs9dJRtUQ==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.36.tgz", + "integrity": "sha512-3YsMWd7V+WZEjbfBnLkkz/olcRBa8nyoK0iIOnNARJBMcYaJxjkJSMZpmSojCnIVwvjA1N83CPAbUL+W+fCnHg==", "cpu": [ "arm" ], @@ -4968,9 +5037,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.35.tgz", - "integrity": "sha512-hx65Qz+G4iG/IVtxJKewC5SJdki8PAPFGl6gC/57Jb0+jA4BIoGLD/J3Q3rCPeoHfdqpkCYpahtyUq8CKx41Jg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.36.tgz", + "integrity": "sha512-lqM3aBB7kJazJYOwHeA5OGNLqXoQPZ/76b3dV+XcjN1GhD0CcXz6mW5PRYVin6OSN1eKrKBKJjtDA1mqADDEvw==", "cpu": [ "arm64" ], @@ -4984,9 +5053,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.35.tgz", - "integrity": "sha512-kL6tQL9No7UEoEvDRuPxzPTpxrvbwYteNRbdChSSP74j13/55G2/2hLmult5yFFaWuyoyU/2lvzjRL/i8OLZxg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.36.tgz", + "integrity": "sha512-bqei2YDzvUfG0pth5W2xJaj0eG4XWYk0d/NJ75vBX6bkIzK6dC8iuKQ41jOfUWonnrAs7rTDDJW0sTn/evvRdw==", "cpu": [ "arm64" ], @@ -5000,9 +5069,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.35.tgz", - "integrity": "sha512-Ke4rcLQSwCQ2LHdJX1FtnqmYNQ3IX6BddKlUtS7mcK13IHkQzZWp0Dcu6MgNA3twzb/dBpKX5GLy07XdGgfmyw==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.36.tgz", + "integrity": "sha512-03maXTUyaBjeCxlDltmdzHje1ryQt1C4OWmmNgSSRXjLb+GNnAenwOJMSrcvHP/aNClD2pwsFCnYKDGy+sYE6w==", "cpu": [ "x64" ], @@ -5016,9 +5085,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.35.tgz", - "integrity": "sha512-T30tlLnz0kYyDFyO5RQF5EQ4ENjW9+b56hEGgFUYmfhFhGA4E4V67iEx7KIG4u0whdPG7oy3qjyyIeTb7nElEw==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.36.tgz", + "integrity": "sha512-XXysqLkvjtQnXm1zHqLhy00UYPv/gk5OtwR732X+piNisnEbcJBqI8Qp9O7YvLWllRcoP8IMBGDWLGdGLSpViA==", "cpu": [ "x64" ], @@ -5032,9 +5101,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.35.tgz", - "integrity": "sha512-CfM/k8mvtuMyX+okRhemfLt784PLS0KF7Q9djA8/Dtavk0L5Ghnq+XsGltO3d8B8+XZ7YOITsB14CrjehzeHsg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.36.tgz", + "integrity": "sha512-k7+dmb13a/zPw+E4XYfPmLZFWJgcOcBRKIjYl9nQErtYsgsg3Ji6TBbsvJVETy23lNHyewZ17V5Vq6NzaG0hzg==", "cpu": [ "arm64" ], @@ -5048,9 +5117,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.35.tgz", - "integrity": "sha512-ATB3uuH8j/RmS64EXQZJSbo2WXfRNpTnQszHME/sGaexsuxeijrp3DTYSFAA3R2Bu6HbIIX6jempe1Au8I3j+A==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.36.tgz", + "integrity": "sha512-ridD3ay6YM2PEYHZXXFN+edYEv0FOynaqOBP+NSnGNHA35azItIjoIe+KNi4WltGtAjpKCHSpjGCNfna12wdYQ==", "cpu": [ "ia32" ], @@ -5064,9 +5133,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.35.tgz", - "integrity": "sha512-iDGfQO1571NqWUXtLYDhwIELA/wadH42ioGn+J9R336nWx40YICzy9UQyslWRhqzhQ5kT+QXAW/MoCWc058N6Q==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.36.tgz", + "integrity": "sha512-j1z2Z1Ln9d0E3dHsPkC1K9XDh0ojhRPwV+GfRTu4D61PE+aYhYLvbJC6xPvL4/204QrStRS7eDu3m+BcDp3rgQ==", "cpu": [ "x64" ], @@ -5425,9 +5494,9 @@ } }, "node_modules/@types/node": { - "version": "20.16.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", - "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "version": "20.16.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.13.tgz", + "integrity": "sha512-GjQ7im10B0labo8ZGXDGROUl9k0BNyDgzfGpb4g/cl+4yYDWVKcozANF4FGr4/p0O/rAkQClM6Wiwkije++1Tg==", "dependencies": { "undici-types": "~6.19.2" } @@ -5671,16 +5740,16 @@ "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", + "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/type-utils": "8.10.0", + "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -5704,15 +5773,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", + "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4" }, "engines": { @@ -5732,13 +5801,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", + "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5749,13 +5818,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", + "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/utils": "8.10.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -5773,9 +5842,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", + "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5786,13 +5855,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", + "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -5838,15 +5907,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", + "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5860,12 +5929,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", + "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/types": "8.10.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -5877,9 +5946,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.2.tgz", - "integrity": "sha512-b7kHrFrs2urS0cOk5N10lttI8UdJ/yP3nB4JYTREvR5o18cR99yPpK4gK8oQgI42BVv0ILWYUSYB7AXkAUDc0g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.3.tgz", + "integrity": "sha512-2OJ3c7UPoFSmBZwqD2VEkUw6A/tzPF0LmW0ZZhhB8PFxuc+9IBG/FaSM+RLEenc7ljzFvGN+G0nGQoZnh7sy2A==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -5899,8 +5968,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.2", - "vitest": "2.1.2" + "@vitest/browser": "2.1.3", + "vitest": "2.1.3" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -5909,22 +5978,22 @@ } }, "node_modules/@vitest/coverage-v8/node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/@vitest/expect": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.2.tgz", - "integrity": "sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz", + "integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==", "dev": true, "dependencies": { - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -5933,12 +6002,12 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.2.tgz", - "integrity": "sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz", + "integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==", "dev": true, "dependencies": { - "@vitest/spy": "^2.1.0-beta.1", + "@vitest/spy": "2.1.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.11" }, @@ -5946,7 +6015,7 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/spy": "2.1.2", + "@vitest/spy": "2.1.3", "msw": "^2.3.5", "vite": "^5.0.0" }, @@ -5969,9 +6038,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.2.tgz", - "integrity": "sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", + "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "dev": true, "dependencies": { "tinyrainbow": "^1.2.0" @@ -5981,12 +6050,12 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.2.tgz", - "integrity": "sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz", + "integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==", "dev": true, "dependencies": { - "@vitest/utils": "2.1.2", + "@vitest/utils": "2.1.3", "pathe": "^1.1.2" }, "funding": { @@ -5994,12 +6063,12 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.2.tgz", - "integrity": "sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz", + "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "magic-string": "^0.30.11", "pathe": "^1.1.2" }, @@ -6017,9 +6086,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.2.tgz", - "integrity": "sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz", + "integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==", "dev": true, "dependencies": { "tinyspy": "^3.0.0" @@ -6029,12 +6098,12 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.2.tgz", - "integrity": "sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz", + "integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -6879,15 +6948,15 @@ } }, "node_modules/bullmq": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-4.17.0.tgz", - "integrity": "sha512-URnHgB01rlCP8RTpmW3kFnvv3vdd2aI1OcBMYQwnqODxGiJUlz9MibDVXE83mq7ee1eS1IvD9lMQqGszX6E5Pw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-4.18.0.tgz", + "integrity": "sha512-TJiS8YaO3GpaiSum3EufaXHnZc7Q5+BjJP1+kM8pD/DAB7BzI44RHZh9yEHgFHI0ikwmHzULIA9zUqkF3L53Ag==", "dependencies": { "cron-parser": "^4.6.0", "glob": "^8.0.3", "ioredis": "^5.3.2", "lodash": "^4.17.21", - "msgpackr": "^1.10.1", + "msgpackr": "^1.6.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", "tslib": "^2.0.0", @@ -6906,6 +6975,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -7329,9 +7399,9 @@ } }, "node_modules/comment-json": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", - "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", + "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", "dev": true, "dependencies": { "array-timsort": "^1.0.3", @@ -8592,16 +8662,16 @@ ] }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -8633,9 +8703,9 @@ } }, "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -10499,9 +10569,9 @@ "dev": true }, "node_modules/mock-fs": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.3.0.tgz", - "integrity": "sha512-IMvz1X+RF7vf+ur7qUenXMR7/FSKSIqS3HqFHXcyNI7G0FbpFO8L5lfsUJhl+bhK1AiulVHWKUSxebWauPA+xQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.0.tgz", + "integrity": "sha512-3ROPnEMgBOkusBMYQUW2rnT3wZwsgfOKzJDLvx/TZ7FL1WmWvwSwn3j4aDR5fLDGtgcc1WF0Z1y0di7c9L4FKw==", "dev": true, "engines": { "node": ">=12.0.0" @@ -14906,9 +14976,9 @@ } }, "node_modules/vite-node": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.2.tgz", - "integrity": "sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz", + "integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -14946,18 +15016,18 @@ } }, "node_modules/vitest": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.2.tgz", - "integrity": "sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz", + "integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==", "dev": true, "dependencies": { - "@vitest/expect": "2.1.2", - "@vitest/mocker": "2.1.2", - "@vitest/pretty-format": "^2.1.2", - "@vitest/runner": "2.1.2", - "@vitest/snapshot": "2.1.2", - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/expect": "2.1.3", + "@vitest/mocker": "2.1.3", + "@vitest/pretty-format": "^2.1.3", + "@vitest/runner": "2.1.3", + "@vitest/snapshot": "2.1.3", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "debug": "^4.3.6", "magic-string": "^0.30.11", @@ -14968,7 +15038,7 @@ "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.2", + "vite-node": "2.1.3", "why-is-node-running": "^2.3.0" }, "bin": { @@ -14983,8 +15053,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.2", - "@vitest/ui": "2.1.2", + "@vitest/browser": "2.1.3", + "@vitest/ui": "2.1.3", "happy-dom": "*", "jsdom": "*" }, @@ -15010,9 +15080,9 @@ } }, "node_modules/vitest/node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -16579,9 +16649,9 @@ } }, "@nestjs/common": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.4.tgz", - "integrity": "sha512-0j2/zqRw9nvHV1GKTktER8B/hIC/Z8CYFjN/ZqUuvwayCH+jZZBhCR2oRyuvLTXdnlSmtCAg2xvQ0ULqQvzqhA==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.5.tgz", + "integrity": "sha512-N/yUyuYCBMb0+H6jHhntR7PURzji0usID/DByhOfooyk/aPGscI0aQKwOA6edlJlT92hHUvXYLJ5p3npj7KcjQ==", "requires": { "iterare": "1.2.1", "tslib": "2.7.0", @@ -16606,9 +16676,9 @@ } }, "@nestjs/core": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.4.tgz", - "integrity": "sha512-y9tjmAzU6LTh1cC/lWrRsCcOd80khSR0qAHAqwY2svbW+AhsR/XCzgpZrAAKJrm/dDfjLCZKyxJSayeirGcW5Q==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.5.tgz", + "integrity": "sha512-wk0KJ+6tuidqAdeemsQ40BCp1BgMsSuSLG577aqXLxXYoa8FQYPrdxoSzd05znYLwJYM55fisZWb3FLF9HT2qw==", "requires": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -16640,13 +16710,13 @@ "requires": {} }, "@nestjs/platform-express": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.4.tgz", - "integrity": "sha512-y52q1MxhbHaT3vAgWd08RgiYon0lJgtTa8U6g6gV0KI0IygwZhDQFJVxnrRDUdxQGIP5CKHmfQu3sk9gTNFoEA==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.5.tgz", + "integrity": "sha512-a629r8R8KC4skhdieQ0aIWH5vDBUFntWnWKFyDXQrll6/CllSchfWm87mWF39seaW6bXYtQtAEZY66JrngdrGA==", "requires": { "body-parser": "1.20.3", "cors": "2.8.5", - "express": "4.21.0", + "express": "4.21.1", "multer": "1.4.4-lts.1", "tslib": "2.7.0" }, @@ -16659,9 +16729,9 @@ } }, "@nestjs/platform-socket.io": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.4.tgz", - "integrity": "sha512-5GEYUA3sNbX2jOBP6FmrIK/zv9VCdvpdr4Sef1OKvt1U0qsV1YgmWPWDPumZM77n5DI0VHSJPyo7yjZaEKWOiQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.4.5.tgz", + "integrity": "sha512-dHkHJQArhrpkX6qBdTW2ghuja3i3cCslwy4QHY6d46u+9UyANQlsNK9wt/lZnmXfCMaci8xAJvUpyODa6YtV7g==", "requires": { "socket.io": "4.7.5", "tslib": "2.7.0" @@ -16691,23 +16761,72 @@ } }, "@nestjs/schematics": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.4.tgz", - "integrity": "sha512-QpY8ez9cTvXXPr3/KBrtSgXQHMSV6BkOUYy2c2TTe6cBqriEdGnCYqGl8cnfrQl3632q3lveQPaZ/c127dHsEw==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.2.tgz", + "integrity": "sha512-D4pJ46E8llCA7WPr3cV6sfRqDlvnTjQWnF1fLyKYD3Ldl+KPtlLyIcxaqlLTB0YR9ItKNKIZTJzUehRxR7UUsQ==", "dev": true, "requires": { - "@angular-devkit/core": "17.3.8", - "@angular-devkit/schematics": "17.3.8", - "comment-json": "4.2.3", + "@angular-devkit/core": "17.3.10", + "@angular-devkit/schematics": "17.3.10", + "comment-json": "4.2.5", "jsonc-parser": "3.3.1", "pluralize": "8.0.0" }, "dependencies": { + "@angular-devkit/core": { + "version": "17.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.10.tgz", + "integrity": "sha512-czdl54yxU5DOAGy/uUPNjJruoBDTgwi/V+eOgLNybYhgrc+TsY0f7uJ11yEk/pz5sCov7xIiS7RdRv96waS7vg==", + "dev": true, + "requires": { + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "dependencies": { + "jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + } + } + }, + "@angular-devkit/schematics": { + "version": "17.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.10.tgz", + "integrity": "sha512-FHcNa1ktYRd0SKExCsNJpR75RffsyuPIV8kvBXzXnLHmXMqvl25G2te3yYJ9yYqy9OLy/58HZznZTxWRyUdHOg==", + "dev": true, + "requires": { + "@angular-devkit/core": "17.3.10", + "jsonc-parser": "3.2.1", + "magic-string": "0.30.8", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "dependencies": { + "jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + } + } + }, "jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true + }, + "picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "dev": true } } }, @@ -16725,9 +16844,9 @@ } }, "@nestjs/testing": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.4.tgz", - "integrity": "sha512-qRGFj51A5RM7JqA8pcyEwSLA3Y0dle/PAZ8oxP0suimoCusRY3Tk7wYqutZdCNj1ATb678SDaUZDHk2pwSv9/g==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.5.tgz", + "integrity": "sha512-3NhmztE+fK3MuuOZhXihvMIhxm0QuDM2BneHvM5A0oVLG+STsAeGBqbDr/Ef2qsvqH5HaqvfGbVJ4N1DQnZE5A==", "dev": true, "requires": { "tslib": "2.7.0" @@ -16750,9 +16869,9 @@ } }, "@nestjs/websockets": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.4.tgz", - "integrity": "sha512-ZHnak04i/iKBS0csjJa7K6D6xdsB0Yz6duJuCR7xGLItchFK+Ne21m9rEF8ffvW74U7UAYkQHBgD5242LBBYiQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.5.tgz", + "integrity": "sha512-LbL/HRLWQUBTUPY7swojOHdvokyVGINIiuP/VmRdhob4T751r+9i09z2RqRpP71psuom9mnRHYI1+vT2FABrAw==", "requires": { "iterare": "1.2.1", "object-hash": "3.0.0", @@ -16877,9 +16996,9 @@ } }, "@opentelemetry/auto-instrumentations-node": { - "version": "0.50.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.50.2.tgz", - "integrity": "sha512-l1JWvNp5gt5Fze8X68+zjzBqiviB5B8zeepsbfpFgdDxoCVjmixg8gcMt/AmqI9Qntw2qaeXah84V14fCbVuMg==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.51.0.tgz", + "integrity": "sha512-xsgydgtJiToxvFsDcmLDrHiFfHOmdomqk4KCnr40YZdsfw7KO4RJEU0om2f7pFh6WUI5q8nSQ53QgZ+DAz6TzA==", "requires": { "@opentelemetry/instrumentation": "^0.53.0", "@opentelemetry/instrumentation-amqplib": "^0.42.0", @@ -16911,7 +17030,7 @@ "@opentelemetry/instrumentation-mysql2": "^0.41.0", "@opentelemetry/instrumentation-nestjs-core": "^0.40.0", "@opentelemetry/instrumentation-net": "^0.39.0", - "@opentelemetry/instrumentation-pg": "^0.45.1", + "@opentelemetry/instrumentation-pg": "^0.46.0", "@opentelemetry/instrumentation-pino": "^0.42.0", "@opentelemetry/instrumentation-redis": "^0.42.0", "@opentelemetry/instrumentation-redis-4": "^0.42.1", @@ -16924,7 +17043,7 @@ "@opentelemetry/resource-detector-alibaba-cloud": "^0.29.3", "@opentelemetry/resource-detector-aws": "^1.6.2", "@opentelemetry/resource-detector-azure": "^0.2.11", - "@opentelemetry/resource-detector-container": "^0.4.3", + "@opentelemetry/resource-detector-container": "^0.4.4", "@opentelemetry/resource-detector-gcp": "^0.29.12", "@opentelemetry/resources": "^1.24.0", "@opentelemetry/sdk-node": "^0.53.0" @@ -17513,9 +17632,9 @@ } }, "@opentelemetry/instrumentation-pg": { - "version": "0.45.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.45.1.tgz", - "integrity": "sha512-GHUvPv7CQEK3RKHH3YAj6mjgJ3nZb6wRQS+t0yaRgKZzX2ggGsLN6OhRT04+IjqmMg9aIRUy1CzqwzgqAxjYbw==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.46.0.tgz", + "integrity": "sha512-PLbYYC7EIoigh9uzhBrDjyL4yhH9akjV2Mln3ci9+lD7p9HE5nUUgYCgcUasyr4bz99c8xy9ErzKLt38Y7Kodg==", "requires": { "@opentelemetry/core": "^1.26.0", "@opentelemetry/instrumentation": "^0.53.0", @@ -17673,9 +17792,9 @@ } }, "@opentelemetry/resource-detector-container": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.4.3.tgz", - "integrity": "sha512-trgLpifOb3rk1l5ygqLlTFoEu7O//q4qvV0Kd5p1D/+WaEDMrp5c6ktJRGrUaU+eBQmlEjwr9BdV6SRnD2lvsA==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.4.4.tgz", + "integrity": "sha512-ZEN2mq7lIjQWJ8NTt1umtr6oT/Kb89856BOmESLSvgSHbIwOFYs7cSfSRH5bfiVw6dXTQAVbZA/wLgCHKrebJA==", "requires": { "@opentelemetry/core": "^1.26.0", "@opentelemetry/resources": "^1.10.0", @@ -18345,92 +18464,92 @@ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, "@swc/core": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.35.tgz", - "integrity": "sha512-3cUteCTbr2r5jqfgx0r091sfq5Mgh6F1SQh8XAOnSvtKzwv2bC31mvBHVAieD1uPa2kHJhLav20DQgXOhpEitw==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.36.tgz", + "integrity": "sha512-bu7ymMX+LCJOSSrKank25Jaq66ymLVA9fOUuy4ck3/6rbXdLw+pIJPnIDKQ9uNcxww8KDxOuJk9Ui9pqR+aGFw==", "devOptional": true, "requires": { - "@swc/core-darwin-arm64": "1.7.35", - "@swc/core-darwin-x64": "1.7.35", - "@swc/core-linux-arm-gnueabihf": "1.7.35", - "@swc/core-linux-arm64-gnu": "1.7.35", - "@swc/core-linux-arm64-musl": "1.7.35", - "@swc/core-linux-x64-gnu": "1.7.35", - "@swc/core-linux-x64-musl": "1.7.35", - "@swc/core-win32-arm64-msvc": "1.7.35", - "@swc/core-win32-ia32-msvc": "1.7.35", - "@swc/core-win32-x64-msvc": "1.7.35", + "@swc/core-darwin-arm64": "1.7.36", + "@swc/core-darwin-x64": "1.7.36", + "@swc/core-linux-arm-gnueabihf": "1.7.36", + "@swc/core-linux-arm64-gnu": "1.7.36", + "@swc/core-linux-arm64-musl": "1.7.36", + "@swc/core-linux-x64-gnu": "1.7.36", + "@swc/core-linux-x64-musl": "1.7.36", + "@swc/core-win32-arm64-msvc": "1.7.36", + "@swc/core-win32-ia32-msvc": "1.7.36", + "@swc/core-win32-x64-msvc": "1.7.36", "@swc/counter": "^0.1.3", "@swc/types": "^0.1.13" } }, "@swc/core-darwin-arm64": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.35.tgz", - "integrity": "sha512-BQSSozVxjxS+SVQz6e3GC/+OBWGIK3jfe52pWdANmycdjF3ch7lrCKTHTU7eHwyoJ96mofszPf5AsiVJF34Fwg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.36.tgz", + "integrity": "sha512-8vDczXzCgv3ceTPhEivlpGprN44YlrCK1nbfU9g2TrhV/Aiqi09W/eM5zLesdoM1Z3mJl492gc/8nlTkpDdusw==", "dev": true, "optional": true }, "@swc/core-darwin-x64": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.35.tgz", - "integrity": "sha512-44TYdKN/EWtkU88foXR7IGki9JzhEJzaFOoPevfi9Xe7hjAD/x2+AJOWWqQNzDPMz9+QewLdUVLyR6s5okRgtg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.36.tgz", + "integrity": "sha512-Pa2Gao7+Wf5m3SsK4abKRtd48AtoUnJInvaC3d077swBfgZjbjUbQvcpdc2dOeQtWwo49rFqUZJonMsL0jnPgQ==", "dev": true, "optional": true }, "@swc/core-linux-arm-gnueabihf": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.35.tgz", - "integrity": "sha512-ccfA5h3zxwioD+/z/AmYtkwtKz9m4rWTV7RoHq6Jfsb0cXHrd6tbcvgqRWXra1kASlE+cDWsMtEZygs9dJRtUQ==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.36.tgz", + "integrity": "sha512-3YsMWd7V+WZEjbfBnLkkz/olcRBa8nyoK0iIOnNARJBMcYaJxjkJSMZpmSojCnIVwvjA1N83CPAbUL+W+fCnHg==", "dev": true, "optional": true }, "@swc/core-linux-arm64-gnu": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.35.tgz", - "integrity": "sha512-hx65Qz+G4iG/IVtxJKewC5SJdki8PAPFGl6gC/57Jb0+jA4BIoGLD/J3Q3rCPeoHfdqpkCYpahtyUq8CKx41Jg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.36.tgz", + "integrity": "sha512-lqM3aBB7kJazJYOwHeA5OGNLqXoQPZ/76b3dV+XcjN1GhD0CcXz6mW5PRYVin6OSN1eKrKBKJjtDA1mqADDEvw==", "dev": true, "optional": true }, "@swc/core-linux-arm64-musl": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.35.tgz", - "integrity": "sha512-kL6tQL9No7UEoEvDRuPxzPTpxrvbwYteNRbdChSSP74j13/55G2/2hLmult5yFFaWuyoyU/2lvzjRL/i8OLZxg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.36.tgz", + "integrity": "sha512-bqei2YDzvUfG0pth5W2xJaj0eG4XWYk0d/NJ75vBX6bkIzK6dC8iuKQ41jOfUWonnrAs7rTDDJW0sTn/evvRdw==", "dev": true, "optional": true }, "@swc/core-linux-x64-gnu": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.35.tgz", - "integrity": "sha512-Ke4rcLQSwCQ2LHdJX1FtnqmYNQ3IX6BddKlUtS7mcK13IHkQzZWp0Dcu6MgNA3twzb/dBpKX5GLy07XdGgfmyw==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.36.tgz", + "integrity": "sha512-03maXTUyaBjeCxlDltmdzHje1ryQt1C4OWmmNgSSRXjLb+GNnAenwOJMSrcvHP/aNClD2pwsFCnYKDGy+sYE6w==", "dev": true, "optional": true }, "@swc/core-linux-x64-musl": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.35.tgz", - "integrity": "sha512-T30tlLnz0kYyDFyO5RQF5EQ4ENjW9+b56hEGgFUYmfhFhGA4E4V67iEx7KIG4u0whdPG7oy3qjyyIeTb7nElEw==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.36.tgz", + "integrity": "sha512-XXysqLkvjtQnXm1zHqLhy00UYPv/gk5OtwR732X+piNisnEbcJBqI8Qp9O7YvLWllRcoP8IMBGDWLGdGLSpViA==", "dev": true, "optional": true }, "@swc/core-win32-arm64-msvc": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.35.tgz", - "integrity": "sha512-CfM/k8mvtuMyX+okRhemfLt784PLS0KF7Q9djA8/Dtavk0L5Ghnq+XsGltO3d8B8+XZ7YOITsB14CrjehzeHsg==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.36.tgz", + "integrity": "sha512-k7+dmb13a/zPw+E4XYfPmLZFWJgcOcBRKIjYl9nQErtYsgsg3Ji6TBbsvJVETy23lNHyewZ17V5Vq6NzaG0hzg==", "dev": true, "optional": true }, "@swc/core-win32-ia32-msvc": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.35.tgz", - "integrity": "sha512-ATB3uuH8j/RmS64EXQZJSbo2WXfRNpTnQszHME/sGaexsuxeijrp3DTYSFAA3R2Bu6HbIIX6jempe1Au8I3j+A==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.36.tgz", + "integrity": "sha512-ridD3ay6YM2PEYHZXXFN+edYEv0FOynaqOBP+NSnGNHA35azItIjoIe+KNi4WltGtAjpKCHSpjGCNfna12wdYQ==", "dev": true, "optional": true }, "@swc/core-win32-x64-msvc": { - "version": "1.7.35", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.35.tgz", - "integrity": "sha512-iDGfQO1571NqWUXtLYDhwIELA/wadH42ioGn+J9R336nWx40YICzy9UQyslWRhqzhQ5kT+QXAW/MoCWc058N6Q==", + "version": "1.7.36", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.36.tgz", + "integrity": "sha512-j1z2Z1Ln9d0E3dHsPkC1K9XDh0ojhRPwV+GfRTu4D61PE+aYhYLvbJC6xPvL4/204QrStRS7eDu3m+BcDp3rgQ==", "dev": true, "optional": true }, @@ -18771,9 +18890,9 @@ } }, "@types/node": { - "version": "20.16.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", - "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "version": "20.16.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.13.tgz", + "integrity": "sha512-GjQ7im10B0labo8ZGXDGROUl9k0BNyDgzfGpb4g/cl+4yYDWVKcozANF4FGr4/p0O/rAkQClM6Wiwkije++1Tg==", "requires": { "undici-types": "~6.19.2" } @@ -19003,16 +19122,16 @@ "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, "@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", + "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/type-utils": "8.10.0", + "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -19020,54 +19139,54 @@ } }, "@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", + "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", + "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", "dev": true, "requires": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0" } }, "@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", + "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/utils": "8.10.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", + "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", + "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", "dev": true, "requires": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -19097,31 +19216,31 @@ } }, "@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", + "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", + "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", "dev": true, "requires": { - "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/types": "8.10.0", "eslint-visitor-keys": "^3.4.3" } }, "@vitest/coverage-v8": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.2.tgz", - "integrity": "sha512-b7kHrFrs2urS0cOk5N10lttI8UdJ/yP3nB4JYTREvR5o18cR99yPpK4gK8oQgI42BVv0ILWYUSYB7AXkAUDc0g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.3.tgz", + "integrity": "sha512-2OJ3c7UPoFSmBZwqD2VEkUw6A/tzPF0LmW0ZZhhB8PFxuc+9IBG/FaSM+RLEenc7ljzFvGN+G0nGQoZnh7sy2A==", "dev": true, "requires": { "@ampproject/remapping": "^2.3.0", @@ -19139,9 +19258,9 @@ }, "dependencies": { "magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "requires": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -19150,24 +19269,24 @@ } }, "@vitest/expect": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.2.tgz", - "integrity": "sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz", + "integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==", "dev": true, "requires": { - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" } }, "@vitest/mocker": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.2.tgz", - "integrity": "sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz", + "integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==", "dev": true, "requires": { - "@vitest/spy": "^2.1.0-beta.1", + "@vitest/spy": "2.1.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.11" }, @@ -19184,31 +19303,31 @@ } }, "@vitest/pretty-format": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.2.tgz", - "integrity": "sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", + "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "dev": true, "requires": { "tinyrainbow": "^1.2.0" } }, "@vitest/runner": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.2.tgz", - "integrity": "sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz", + "integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==", "dev": true, "requires": { - "@vitest/utils": "2.1.2", + "@vitest/utils": "2.1.3", "pathe": "^1.1.2" } }, "@vitest/snapshot": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.2.tgz", - "integrity": "sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz", + "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==", "dev": true, "requires": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "magic-string": "^0.30.11", "pathe": "^1.1.2" }, @@ -19225,21 +19344,21 @@ } }, "@vitest/spy": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.2.tgz", - "integrity": "sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz", + "integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==", "dev": true, "requires": { "tinyspy": "^3.0.0" } }, "@vitest/utils": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.2.tgz", - "integrity": "sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz", + "integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==", "dev": true, "requires": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" } @@ -19894,15 +20013,15 @@ "dev": true }, "bullmq": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-4.17.0.tgz", - "integrity": "sha512-URnHgB01rlCP8RTpmW3kFnvv3vdd2aI1OcBMYQwnqODxGiJUlz9MibDVXE83mq7ee1eS1IvD9lMQqGszX6E5Pw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-4.18.0.tgz", + "integrity": "sha512-TJiS8YaO3GpaiSum3EufaXHnZc7Q5+BjJP1+kM8pD/DAB7BzI44RHZh9yEHgFHI0ikwmHzULIA9zUqkF3L53Ag==", "requires": { "cron-parser": "^4.6.0", "glob": "^8.0.3", "ioredis": "^5.3.2", "lodash": "^4.17.21", - "msgpackr": "^1.10.1", + "msgpackr": "^1.6.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", "tslib": "^2.0.0", @@ -20214,9 +20333,9 @@ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" }, "comment-json": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", - "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", + "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", "dev": true, "requires": { "array-timsort": "^1.0.3", @@ -21123,16 +21242,16 @@ "optional": true }, "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -21161,9 +21280,9 @@ }, "dependencies": { "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" }, "debug": { "version": "2.6.9", @@ -22555,9 +22674,9 @@ "dev": true }, "mock-fs": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.3.0.tgz", - "integrity": "sha512-IMvz1X+RF7vf+ur7qUenXMR7/FSKSIqS3HqFHXcyNI7G0FbpFO8L5lfsUJhl+bhK1AiulVHWKUSxebWauPA+xQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.0.tgz", + "integrity": "sha512-3ROPnEMgBOkusBMYQUW2rnT3wZwsgfOKzJDLvx/TZ7FL1WmWvwSwn3j4aDR5fLDGtgcc1WF0Z1y0di7c9L4FKw==", "dev": true }, "module-details-from-path": { @@ -25528,9 +25647,9 @@ } }, "vite-node": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.2.tgz", - "integrity": "sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz", + "integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==", "dev": true, "requires": { "cac": "^6.7.14", @@ -25551,18 +25670,18 @@ } }, "vitest": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.2.tgz", - "integrity": "sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz", + "integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==", "dev": true, "requires": { - "@vitest/expect": "2.1.2", - "@vitest/mocker": "2.1.2", - "@vitest/pretty-format": "^2.1.2", - "@vitest/runner": "2.1.2", - "@vitest/snapshot": "2.1.2", - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/expect": "2.1.3", + "@vitest/mocker": "2.1.3", + "@vitest/pretty-format": "^2.1.3", + "@vitest/runner": "2.1.3", + "@vitest/snapshot": "2.1.3", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "debug": "^4.3.6", "magic-string": "^0.30.11", @@ -25573,14 +25692,14 @@ "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.2", + "vite-node": "2.1.3", "why-is-node-running": "^2.3.0" }, "dependencies": { "magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "requires": { "@jridgewell/sourcemap-codec": "^1.5.0" diff --git a/server/package.json b/server/package.json index 1ef34647bc..39a3f78d0e 100644 --- a/server/package.json +++ b/server/package.json @@ -45,7 +45,7 @@ "@nestjs/swagger": "^7.1.8", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.2", - "@opentelemetry/auto-instrumentations-node": "^0.50.0", + "@opentelemetry/auto-instrumentations-node": "^0.51.0", "@opentelemetry/context-async-hooks": "^1.24.0", "@opentelemetry/exporter-prometheus": "^0.53.0", "@opentelemetry/sdk-node": "^0.53.0", @@ -108,7 +108,7 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^3.0.0", "@types/pngjs": "^6.0.5", diff --git a/server/src/app.module.ts b/server/src/app.module.ts index fd921150fd..3c26faaca3 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -14,6 +14,7 @@ import { entities } from 'src/entities'; import { ImmichWorker } from 'src/enum'; import { IEventRepository } from 'src/interfaces/event.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; import { AuthGuard } from 'src/middleware/auth.guard'; import { ErrorInterceptor } from 'src/middleware/error.interceptor'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; @@ -21,6 +22,7 @@ import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter'; import { LoggingInterceptor } from 'src/middleware/logging.interceptor'; import { repositories } from 'src/repositories'; import { ConfigRepository } from 'src/repositories/config.repository'; +import { teardownTelemetry } from 'src/repositories/telemetry.repository'; import { services } from 'src/services'; import { DatabaseService } from 'src/services/database.service'; @@ -66,6 +68,7 @@ abstract class BaseModule implements OnModuleInit, OnModuleDestroy { constructor( @Inject(ILoggerRepository) logger: ILoggerRepository, @Inject(IEventRepository) private eventRepository: IEventRepository, + @Inject(ITelemetryRepository) private telemetryRepository: ITelemetryRepository, ) { logger.setAppName(this.worker); } @@ -73,12 +76,14 @@ abstract class BaseModule implements OnModuleInit, OnModuleDestroy { abstract getWorker(): ImmichWorker; async onModuleInit() { + this.telemetryRepository.setup({ repositories: repositories.map(({ useClass }) => useClass) }); this.eventRepository.setup({ services }); await this.eventRepository.emit('app.bootstrap', this.worker); } async onModuleDestroy() { await this.eventRepository.emit('app.shutdown', this.worker); + await teardownTelemetry(); } } diff --git a/server/src/config.ts b/server/src/config.ts index e386c134b4..fca6719bc0 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -354,9 +354,9 @@ export const immichAppConfig: ConfigModuleOptions = { ), IMMICH_METRICS: Joi.boolean().optional().default(false), - IMMICH_HOST_METRICS: Joi.boolean().optional().default(false), - IMMICH_API_METRICS: Joi.boolean().optional().default(false), - IMMICH_IO_METRICS: Joi.boolean().optional().default(false), + IMMICH_HOST_METRICS: Joi.boolean().optional(), + IMMICH_API_METRICS: Joi.boolean().optional(), + IMMICH_IO_METRICS: Joi.boolean().optional(), }), }; diff --git a/server/src/decorators.ts b/server/src/decorators.ts index 2782368239..db755c5ff9 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -86,27 +86,6 @@ export function ChunkedSet(options?: { paramIndex?: number }): MethodDecorator { return Chunked({ ...options, mergeFn: setUnion }); } -// https://stackoverflow.com/a/74898678 -export function DecorateAll( - decorator: ( - target: any, - propertyKey: string, - descriptor: TypedPropertyDescriptor, - ) => TypedPropertyDescriptor | void, -) { - return (target: any) => { - const descriptors = Object.getOwnPropertyDescriptors(target.prototype); - for (const [propName, descriptor] of Object.entries(descriptors)) { - const isMethod = typeof descriptor.value == 'function' && propName !== 'constructor'; - if (!isMethod) { - continue; - } - decorator({ ...target, constructor: { ...target.constructor, name: target.name } as any }, propName, descriptor); - Object.defineProperty(target.prototype, propName, descriptor); - } - }; -} - const UUID = '00000000-0000-4000-a000-000000000000'; export const DummyValue = { @@ -128,6 +107,9 @@ export interface GenerateSqlQueries { params: unknown[]; } +export const Telemetry = (options: { enabled?: boolean }) => + SetMetadata(MetadataKey.TELEMETRY_ENABLED, options?.enabled ?? true); + /** Decorator to enable versioning/tracking of generated Sql */ export const GenerateSql = (...options: GenerateSqlQueries[]) => SetMetadata(GENERATE_SQL_KEY, options); diff --git a/server/src/enum.ts b/server/src/enum.ts index 8c11834dac..902d6635e7 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -334,6 +334,7 @@ export enum MetadataKey { SHARED_ROUTE = 'shared_route', API_KEY_SECURITY = 'api_key', EVENT_CONFIG = 'event_config', + TELEMETRY_ENABLED = 'telemetry_enabled', } export enum RouteKey { diff --git a/server/src/interfaces/event.interface.ts b/server/src/interfaces/event.interface.ts index 7ea48faf53..40efaf150c 100644 --- a/server/src/interfaces/event.interface.ts +++ b/server/src/interfaces/event.interface.ts @@ -22,7 +22,7 @@ type EventMap = { 'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }]; // album events - 'album.update': [{ id: string; updatedBy: string }]; + 'album.update': [{ id: string; recipientIds: string[] }]; 'album.invite': [{ id: string; userId: string }]; // asset events diff --git a/server/src/interfaces/job.interface.ts b/server/src/interfaces/job.interface.ts index aa3090675e..82176ffa93 100644 --- a/server/src/interfaces/job.interface.ts +++ b/server/src/interfaces/job.interface.ts @@ -120,6 +120,11 @@ export interface IBaseJob { force?: boolean; } +export interface IDelayedJob extends IBaseJob { + /** The minimum time to wait to execute this job, in milliseconds. */ + delay?: number; +} + export interface IEntityJob extends IBaseJob { id: string; source?: 'upload' | 'sidecar-write' | 'copy'; @@ -181,8 +186,8 @@ export interface INotifyAlbumInviteJob extends IEntityJob { recipientId: string; } -export interface INotifyAlbumUpdateJob extends IEntityJob { - senderId: string; +export interface INotifyAlbumUpdateJob extends IEntityJob, IDelayedJob { + recipientIds: string[]; } export interface JobCounts { @@ -310,4 +315,5 @@ export interface IJobRepository { getQueueStatus(name: QueueName): Promise; getJobCounts(name: QueueName): Promise; waitForQueueCompletion(...queues: QueueName[]): Promise; + removeJob(jobId: string, name: JobName): Promise; } diff --git a/server/src/interfaces/metric.interface.ts b/server/src/interfaces/telemetry.interface.ts similarity index 71% rename from server/src/interfaces/metric.interface.ts rename to server/src/interfaces/telemetry.interface.ts index a87a849833..688e52c21e 100644 --- a/server/src/interfaces/metric.interface.ts +++ b/server/src/interfaces/telemetry.interface.ts @@ -1,6 +1,7 @@ import { MetricOptions } from '@opentelemetry/api'; +import { ClassConstructor } from 'class-transformer'; -export const IMetricRepository = 'IMetricRepository'; +export const ITelemetryRepository = 'ITelemetryRepository'; export interface MetricGroupOptions { enabled: boolean; @@ -13,7 +14,8 @@ export interface IMetricGroupRepository { configure(options: MetricGroupOptions): this; } -export interface IMetricRepository { +export interface ITelemetryRepository { + setup(options: { repositories: ClassConstructor[] }): void; api: IMetricGroupRepository; host: IMetricGroupRepository; jobs: IMetricGroupRepository; diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index f6921ffe27..f3cbf392db 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -15,7 +15,6 @@ import { StackEntity } from 'src/entities/stack.entity'; import { TagEntity } from 'src/entities/tag.entity'; import { AlbumUserRole } from 'src/enum'; import { IAccessRepository } from 'src/interfaces/access.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Brackets, In, Repository } from 'typeorm'; type IActivityAccess = IAccessRepository['activity']; @@ -29,7 +28,6 @@ type IStackAccess = IAccessRepository['stack']; type ITagAccess = IAccessRepository['tag']; type ITimelineAccess = IAccessRepository['timeline']; -@Instrumentation() @Injectable() class ActivityAccess implements IActivityAccess { constructor( diff --git a/server/src/repositories/activity.repository.ts b/server/src/repositories/activity.repository.ts index e21f746483..0f0a0cb60e 100644 --- a/server/src/repositories/activity.repository.ts +++ b/server/src/repositories/activity.repository.ts @@ -3,7 +3,6 @@ import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { ActivityEntity } from 'src/entities/activity.entity'; import { IActivityRepository } from 'src/interfaces/activity.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { IsNull, Repository } from 'typeorm'; export interface ActivitySearch { @@ -13,7 +12,6 @@ export interface ActivitySearch { isLiked?: boolean; } -@Instrumentation() @Injectable() export class ActivityRepository implements IActivityRepository { constructor(@InjectRepository(ActivityEntity) private repository: Repository) {} diff --git a/server/src/repositories/album-user.repository.ts b/server/src/repositories/album-user.repository.ts index 7fd18711aa..9328ea8cfc 100644 --- a/server/src/repositories/album-user.repository.ts +++ b/server/src/repositories/album-user.repository.ts @@ -2,10 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { AlbumUserEntity } from 'src/entities/album-user.entity'; import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class AlbumUserRepository implements IAlbumUserRepository { constructor(@InjectRepository(AlbumUserEntity) private repository: Repository) {} diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index f7b4cb44aa..8b7565e318 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -4,7 +4,6 @@ import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/ import { AlbumEntity } from 'src/entities/album.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { DataSource, EntityManager, @@ -23,7 +22,6 @@ const withoutDeletedUsers = (album: T) => { return album; }; -@Instrumentation() @Injectable() export class AlbumRepository implements IAlbumRepository { constructor( diff --git a/server/src/repositories/api-key.repository.ts b/server/src/repositories/api-key.repository.ts index 5178039177..bb37390de1 100644 --- a/server/src/repositories/api-key.repository.ts +++ b/server/src/repositories/api-key.repository.ts @@ -3,10 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { APIKeyEntity } from 'src/entities/api-key.entity'; import { IKeyRepository } from 'src/interfaces/api-key.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class ApiKeyRepository implements IKeyRepository { constructor(@InjectRepository(APIKeyEntity) private repository: Repository) {} diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index fd47a976a5..6080e943e4 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -29,7 +29,6 @@ import { } from 'src/interfaces/asset.interface'; import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.interface'; import { searchAssetBuilder } from 'src/utils/database'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Paginated, PaginationOptions, paginate, paginatedBuilder } from 'src/utils/pagination'; import { Brackets, @@ -54,7 +53,6 @@ const dateTrunc = (options: TimeBucketOptions) => truncateMap[options.size] }', (asset."localDateTime" at time zone 'UTC')) at time zone 'UTC')::timestamptz`; -@Instrumentation() @Injectable() export class AssetRepository implements IAssetRepository { constructor( diff --git a/server/src/repositories/audit.repository.ts b/server/src/repositories/audit.repository.ts index deb0d0f6f1..ac73c3a8b9 100644 --- a/server/src/repositories/audit.repository.ts +++ b/server/src/repositories/audit.repository.ts @@ -2,10 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { AuditEntity } from 'src/entities/audit.entity'; import { AuditSearch, IAuditRepository } from 'src/interfaces/audit.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { In, LessThan, MoreThan, Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class AuditRepository implements IAuditRepository { constructor(@InjectRepository(AuditEntity) private repository: Repository) {} diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index 44b8c7b605..fabccd7846 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { join } from 'node:path'; import { citiesFile, excludePaths } from 'src/constants'; +import { Telemetry } from 'src/decorators'; import { ImmichEnvironment, ImmichWorker, LogLevel } from 'src/enum'; import { EnvData, IConfigRepository } from 'src/interfaces/config.interface'; import { DatabaseExtension } from 'src/interfaces/database.interface'; @@ -74,9 +75,6 @@ const getEnv = (): EnvData => { const repoMetrics = parseBoolean(process.env.IMMICH_IO_METRICS, globalEnabled); const jobMetrics = parseBoolean(process.env.IMMICH_JOB_METRICS, globalEnabled); const telemetryEnabled = globalEnabled || hostMetrics || apiMetrics || repoMetrics || jobMetrics; - if (!telemetryEnabled && process.env.OTEL_SDK_DISABLED === undefined) { - process.env.OTEL_SDK_DISABLED = 'true'; - } return { host: process.env.IMMICH_HOST, @@ -186,6 +184,7 @@ const getEnv = (): EnvData => { let cached: EnvData | undefined; @Injectable() +@Telemetry({ enabled: false }) export class ConfigRepository implements IConfigRepository { getEnv(): EnvData { if (!cached) { diff --git a/server/src/repositories/crypto.repository.ts b/server/src/repositories/crypto.repository.ts index 72e75ef174..ee25609fec 100644 --- a/server/src/repositories/crypto.repository.ts +++ b/server/src/repositories/crypto.repository.ts @@ -3,9 +3,7 @@ import { compareSync, hash } from 'bcrypt'; import { createHash, createPublicKey, createVerify, randomBytes, randomUUID } from 'node:crypto'; import { createReadStream } from 'node:fs'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; -@Instrumentation() @Injectable() export class CryptoRepository implements ICryptoRepository { randomUUID() { diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index 547f03fc20..b5e2edfdea 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -15,11 +15,9 @@ import { VectorUpdateResult, } from 'src/interfaces/database.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { isValidInteger } from 'src/validation'; import { DataSource, EntityManager, QueryRunner } from 'typeorm'; -@Instrumentation() @Injectable() export class DatabaseRepository implements IDatabaseRepository { private vectorExtension: VectorExtension; diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index cb58d56b2a..bb265196f9 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -24,7 +24,6 @@ import { } from 'src/interfaces/event.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AuthService } from 'src/services/auth.service'; -import { Instrumentation } from 'src/utils/instrumentation'; import { handlePromiseError } from 'src/utils/misc'; type EmitHandlers = Partial<{ [T in EmitEvent]: Array> }>; @@ -37,7 +36,6 @@ type Item = { label: string; }; -@Instrumentation() @WebSocketGateway({ cors: true, path: '/api/socket.io', diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index 5bf08d0d78..94a0212204 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -17,7 +17,6 @@ import { IMapRepository } from 'src/interfaces/map.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMemoryRepository } from 'src/interfaces/memory.interface'; import { IMetadataRepository } from 'src/interfaces/metadata.interface'; -import { IMetricRepository } from 'src/interfaces/metric.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { INotificationRepository } from 'src/interfaces/notification.interface'; import { IOAuthRepository } from 'src/interfaces/oauth.interface'; @@ -31,6 +30,7 @@ import { IStackRepository } from 'src/interfaces/stack.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ITagRepository } from 'src/interfaces/tag.interface'; +import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; import { ITrashRepository } from 'src/interfaces/trash.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; @@ -54,7 +54,6 @@ import { MapRepository } from 'src/repositories/map.repository'; import { MediaRepository } from 'src/repositories/media.repository'; import { MemoryRepository } from 'src/repositories/memory.repository'; import { MetadataRepository } from 'src/repositories/metadata.repository'; -import { MetricRepository } from 'src/repositories/metric.repository'; import { MoveRepository } from 'src/repositories/move.repository'; import { NotificationRepository } from 'src/repositories/notification.repository'; import { OAuthRepository } from 'src/repositories/oauth.repository'; @@ -68,6 +67,7 @@ import { StackRepository } from 'src/repositories/stack.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { TagRepository } from 'src/repositories/tag.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { TrashRepository } from 'src/repositories/trash.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; @@ -93,7 +93,6 @@ export const repositories = [ { provide: IMediaRepository, useClass: MediaRepository }, { provide: IMemoryRepository, useClass: MemoryRepository }, { provide: IMetadataRepository, useClass: MetadataRepository }, - { provide: IMetricRepository, useClass: MetricRepository }, { provide: IMoveRepository, useClass: MoveRepository }, { provide: INotificationRepository, useClass: NotificationRepository }, { provide: IOAuthRepository, useClass: OAuthRepository }, @@ -107,6 +106,7 @@ export const repositories = [ { provide: IStorageRepository, useClass: StorageRepository }, { provide: ISystemMetadataRepository, useClass: SystemMetadataRepository }, { provide: ITagRepository, useClass: TagRepository }, + { provide: ITelemetryRepository, useClass: TelemetryRepository }, { provide: ITrashRepository, useClass: TrashRepository }, { provide: IUserRepository, useClass: UserRepository }, { provide: IVersionHistoryRepository, useClass: VersionHistoryRepository }, diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index 3ff26f1ba4..2b783e7d2f 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -7,6 +7,7 @@ import { CronJob, CronTime } from 'cron'; import { setTimeout } from 'node:timers/promises'; import { IConfigRepository } from 'src/interfaces/config.interface'; import { + IEntityJob, IJobRepository, JobCounts, JobItem, @@ -16,7 +17,6 @@ import { QueueStatus, } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; export const JOBS_TO_QUEUE: Record = { // misc @@ -98,7 +98,6 @@ export const JOBS_TO_QUEUE: Record = { [JobName.QUEUE_TRASH_EMPTY]: QueueName.BACKGROUND_TASK, }; -@Instrumentation() @Injectable() export class JobRepository implements IJobRepository { private workers: Partial> = {}; @@ -252,6 +251,9 @@ export class JobRepository implements IJobRepository { private getJobOptions(item: JobItem): JobsOptions | null { switch (item.name) { + case JobName.NOTIFY_ALBUM_UPDATE: { + return { jobId: item.data.id, delay: item.data?.delay }; + } case JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE: { return { jobId: item.data.id }; } @@ -261,7 +263,6 @@ export class JobRepository implements IJobRepository { case JobName.QUEUE_FACIAL_RECOGNITION: { return { jobId: JobName.QUEUE_FACIAL_RECOGNITION }; } - default: { return null; } @@ -271,4 +272,20 @@ export class JobRepository implements IJobRepository { private getQueue(queue: QueueName): Queue { return this.moduleReference.get(getQueueToken(queue), { strict: false }); } + + public async removeJob(jobId: string, name: JobName): Promise { + const existingJob = await this.getQueue(JOBS_TO_QUEUE[name]).getJob(jobId); + if (!existingJob) { + return; + } + try { + await existingJob.remove(); + } catch (error: any) { + if (error.message?.includes('Missing key for job')) { + return; + } + throw error; + } + return existingJob.data; + } } diff --git a/server/src/repositories/library.repository.ts b/server/src/repositories/library.repository.ts index 36fb4b9217..1446395854 100644 --- a/server/src/repositories/library.repository.ts +++ b/server/src/repositories/library.repository.ts @@ -4,11 +4,9 @@ import { DummyValue, GenerateSql } from 'src/decorators'; import { LibraryStatsResponseDto } from 'src/dtos/library.dto'; import { LibraryEntity } from 'src/entities/library.entity'; import { ILibraryRepository } from 'src/interfaces/library.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { IsNull, Not } from 'typeorm'; import { Repository } from 'typeorm/repository/Repository.js'; -@Instrumentation() @Injectable() export class LibraryRepository implements ILibraryRepository { constructor(@InjectRepository(LibraryEntity) private repository: Repository) {} diff --git a/server/src/repositories/logger.repository.ts b/server/src/repositories/logger.repository.ts index 2023cd6c43..4f1d3cac22 100644 --- a/server/src/repositories/logger.repository.ts +++ b/server/src/repositories/logger.repository.ts @@ -1,6 +1,7 @@ import { ConsoleLogger, Inject, Injectable, Scope } from '@nestjs/common'; import { isLogLevelEnabled } from '@nestjs/common/services/utils/is-log-level-enabled.util'; import { ClsService } from 'nestjs-cls'; +import { Telemetry } from 'src/decorators'; import { LogLevel } from 'src/enum'; import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -17,6 +18,7 @@ enum LogColor { } @Injectable({ scope: Scope.TRANSIENT }) +@Telemetry({ enabled: false }) export class LoggerRepository extends ConsoleLogger implements ILoggerRepository { private static logLevels: LogLevel[] = [LogLevel.LOG, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL]; private noColor: boolean; diff --git a/server/src/repositories/machine-learning.repository.ts b/server/src/repositories/machine-learning.repository.ts index b9404022ef..74b17ca6a7 100644 --- a/server/src/repositories/machine-learning.repository.ts +++ b/server/src/repositories/machine-learning.repository.ts @@ -12,11 +12,9 @@ import { ModelTask, ModelType, } from 'src/interfaces/machine-learning.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; const errorPrefix = 'Machine learning request'; -@Instrumentation() @Injectable() export class MachineLearningRepository implements IMachineLearningRepository { private async predict(url: string, payload: ModelPayload, config: MachineLearningRequest): Promise { diff --git a/server/src/repositories/map.repository.ts b/server/src/repositories/map.repository.ts index 3e5c499f41..7ad94016e8 100644 --- a/server/src/repositories/map.repository.ts +++ b/server/src/repositories/map.repository.ts @@ -20,11 +20,9 @@ import { } from 'src/interfaces/map.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { OptionalBetween } from 'src/utils/database'; -import { Instrumentation } from 'src/utils/instrumentation'; import { DataSource, In, IsNull, Not, QueryRunner, Repository } from 'typeorm'; import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js'; -@Instrumentation() @Injectable() export class MapRepository implements IMapRepository { constructor( diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index 0777ca3479..7e1ca84993 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -17,7 +17,6 @@ import { TranscodeCommand, VideoInfo, } from 'src/interfaces/media.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { handlePromiseError } from 'src/utils/misc'; const probe = (input: string, options: string[]): Promise => @@ -36,7 +35,6 @@ type ProgressEvent = { percent?: number; }; -@Instrumentation() @Injectable() export class MediaRepository implements IMediaRepository { constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) { @@ -202,7 +200,7 @@ export class MediaRepository implements IMediaRepository { lastProgressFrame = progress.frames; const percent = ((progress.frames / frameCount) * 100).toFixed(2); - const ms = Math.floor((frameCount - progress.frames) / progress.currentFps) * 1000; + const ms = progress.currentFps ? Math.floor((frameCount - progress.frames) / progress.currentFps) * 1000 : 0; const duration = ms ? Duration.fromMillis(ms).rescale().toHuman({ unitDisplay: 'narrow' }) : ''; const outputText = output instanceof Writable ? 'stream' : output.split('/').pop(); this.logger.debug( diff --git a/server/src/repositories/memory.repository.ts b/server/src/repositories/memory.repository.ts index e9b4532fe9..3c2a1ae191 100644 --- a/server/src/repositories/memory.repository.ts +++ b/server/src/repositories/memory.repository.ts @@ -3,10 +3,8 @@ import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { MemoryEntity } from 'src/entities/memory.entity'; import { IMemoryRepository } from 'src/interfaces/memory.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { DataSource, In, Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class MemoryRepository implements IMemoryRepository { constructor( diff --git a/server/src/repositories/metadata.repository.ts b/server/src/repositories/metadata.repository.ts index dc2a4cdf9b..81c1b35e15 100644 --- a/server/src/repositories/metadata.repository.ts +++ b/server/src/repositories/metadata.repository.ts @@ -3,9 +3,7 @@ import { DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored'; import geotz from 'geo-tz'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; -@Instrumentation() @Injectable() export class MetadataRepository implements IMetadataRepository { private exiftool = new ExifTool({ diff --git a/server/src/repositories/metric.repository.ts b/server/src/repositories/metric.repository.ts deleted file mode 100644 index b59bcf9ed1..0000000000 --- a/server/src/repositories/metric.repository.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { MetricOptions } from '@opentelemetry/api'; -import { MetricService } from 'nestjs-otel'; -import { IConfigRepository } from 'src/interfaces/config.interface'; -import { IMetricGroupRepository, IMetricRepository, MetricGroupOptions } from 'src/interfaces/metric.interface'; - -class MetricGroupRepository implements IMetricGroupRepository { - private enabled = false; - - constructor(private metricService: MetricService) {} - - addToCounter(name: string, value: number, options?: MetricOptions): void { - if (this.enabled) { - this.metricService.getCounter(name, options).add(value); - } - } - - addToGauge(name: string, value: number, options?: MetricOptions): void { - if (this.enabled) { - this.metricService.getUpDownCounter(name, options).add(value); - } - } - - addToHistogram(name: string, value: number, options?: MetricOptions): void { - if (this.enabled) { - this.metricService.getHistogram(name, options).record(value); - } - } - - configure(options: MetricGroupOptions): this { - this.enabled = options.enabled; - return this; - } -} - -@Injectable() -export class MetricRepository implements IMetricRepository { - api: MetricGroupRepository; - host: MetricGroupRepository; - jobs: MetricGroupRepository; - repo: MetricGroupRepository; - - constructor(metricService: MetricService, @Inject(IConfigRepository) configRepository: IConfigRepository) { - const { telemetry } = configRepository.getEnv(); - this.api = new MetricGroupRepository(metricService).configure({ enabled: telemetry.apiMetrics }); - this.host = new MetricGroupRepository(metricService).configure({ enabled: telemetry.hostMetrics }); - this.jobs = new MetricGroupRepository(metricService).configure({ enabled: telemetry.jobMetrics }); - this.repo = new MetricGroupRepository(metricService).configure({ enabled: telemetry.repoMetrics }); - } -} diff --git a/server/src/repositories/move.repository.ts b/server/src/repositories/move.repository.ts index 45fd446526..16d9004014 100644 --- a/server/src/repositories/move.repository.ts +++ b/server/src/repositories/move.repository.ts @@ -4,10 +4,8 @@ import { DummyValue, GenerateSql } from 'src/decorators'; import { MoveEntity } from 'src/entities/move.entity'; import { PathType } from 'src/enum'; import { IMoveRepository, MoveCreate } from 'src/interfaces/move.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class MoveRepository implements IMoveRepository { constructor(@InjectRepository(MoveEntity) private repository: Repository) {} diff --git a/server/src/repositories/notification.repository.ts b/server/src/repositories/notification.repository.ts index 9814a7bd5e..293a80576f 100644 --- a/server/src/repositories/notification.repository.ts +++ b/server/src/repositories/notification.repository.ts @@ -15,9 +15,7 @@ import { SendEmailResponse, SmtpOptions, } from 'src/interfaces/notification.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; -@Instrumentation() @Injectable() export class NotificationRepository implements INotificationRepository { constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) { diff --git a/server/src/repositories/oauth.repository.ts b/server/src/repositories/oauth.repository.ts index adde7099d0..ed038f0137 100644 --- a/server/src/repositories/oauth.repository.ts +++ b/server/src/repositories/oauth.repository.ts @@ -2,9 +2,7 @@ import { Inject, Injectable, InternalServerErrorException } from '@nestjs/common import { custom, generators, Issuer } from 'openid-client'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IOAuthRepository, OAuthConfig, OAuthProfile } from 'src/interfaces/oauth.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; -@Instrumentation() @Injectable() export class OAuthRepository implements IOAuthRepository { constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) { diff --git a/server/src/repositories/partner.repository.ts b/server/src/repositories/partner.repository.ts index e0c8998dbf..6b11a4e31e 100644 --- a/server/src/repositories/partner.repository.ts +++ b/server/src/repositories/partner.repository.ts @@ -2,10 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { PartnerEntity } from 'src/entities/partner.entity'; import { IPartnerRepository, PartnerIds } from 'src/interfaces/partner.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { DeepPartial, Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class PartnerRepository implements IPartnerRepository { constructor(@InjectRepository(PartnerEntity) private repository: Repository) {} diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 86927a8ba8..f0bacbdd77 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -21,11 +21,9 @@ import { UnassignFacesOptions, UpdateFacesData, } from 'src/interfaces/person.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Paginated, PaginationOptions, paginate, paginatedBuilder } from 'src/utils/pagination'; import { DataSource, FindManyOptions, FindOptionsRelations, FindOptionsSelect, In, Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class PersonRepository implements IPersonRepository { constructor( diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 882a2634bd..b5969beecb 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -23,12 +23,10 @@ import { SmartSearchOptions, } from 'src/interfaces/search.interface'; import { asVector, searchAssetBuilder } from 'src/utils/database'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Paginated, PaginationResult, paginatedBuilder } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; import { Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class SearchRepository implements ISearchRepository { private vectorExtension: VectorExtension; diff --git a/server/src/repositories/server-info.repository.ts b/server/src/repositories/server-info.repository.ts index 1936ecdb61..b4a4652871 100644 --- a/server/src/repositories/server-info.repository.ts +++ b/server/src/repositories/server-info.repository.ts @@ -7,7 +7,6 @@ import sharp from 'sharp'; import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { GitHubRelease, IServerInfoRepository, ServerBuildVersions } from 'src/interfaces/server-info.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; const exec = promisify(execCallback); const maybeFirstLine = async (command: string): Promise => { @@ -34,7 +33,6 @@ const getLockfileVersion = (name: string, lockfile?: BuildLockfile) => { return item?.version; }; -@Instrumentation() @Injectable() export class ServerInfoRepository implements IServerInfoRepository { constructor( diff --git a/server/src/repositories/session.repository.ts b/server/src/repositories/session.repository.ts index a4b55a19d7..3a0af1ef69 100644 --- a/server/src/repositories/session.repository.ts +++ b/server/src/repositories/session.repository.ts @@ -3,10 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { SessionEntity } from 'src/entities/session.entity'; import { ISessionRepository, SessionSearchOptions } from 'src/interfaces/session.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { LessThanOrEqual, Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class SessionRepository implements ISessionRepository { constructor(@InjectRepository(SessionEntity) private repository: Repository) {} diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index 48dbb3ab90..1dfde99a75 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -3,10 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class SharedLinkRepository implements ISharedLinkRepository { constructor(@InjectRepository(SharedLinkEntity) private repository: Repository) {} diff --git a/server/src/repositories/stack.repository.ts b/server/src/repositories/stack.repository.ts index f23a1c9a9c..ae1a7f70d4 100644 --- a/server/src/repositories/stack.repository.ts +++ b/server/src/repositories/stack.repository.ts @@ -3,10 +3,8 @@ import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; import { AssetEntity } from 'src/entities/asset.entity'; import { StackEntity } from 'src/entities/stack.entity'; import { IStackRepository, StackSearch } from 'src/interfaces/stack.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { DataSource, In, Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class StackRepository implements IStackRepository { constructor( diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index b957449984..1ef0e9d6bf 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -14,10 +14,8 @@ import { ImmichZipStream, WatchEvents, } from 'src/interfaces/storage.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { mimeTypes } from 'src/utils/mime-types'; -@Instrumentation() @Injectable() export class StorageRepository implements IStorageRepository { constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) { diff --git a/server/src/repositories/system-metadata.repository.ts b/server/src/repositories/system-metadata.repository.ts index d4e58bf74a..1c6aaf0517 100644 --- a/server/src/repositories/system-metadata.repository.ts +++ b/server/src/repositories/system-metadata.repository.ts @@ -3,10 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm'; import { readFile } from 'node:fs/promises'; import { SystemMetadata, SystemMetadataEntity } from 'src/entities/system-metadata.entity'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class SystemMetadataRepository implements ISystemMetadataRepository { constructor( diff --git a/server/src/repositories/tag.repository.ts b/server/src/repositories/tag.repository.ts index 1a5415b8db..df5f7e6e42 100644 --- a/server/src/repositories/tag.repository.ts +++ b/server/src/repositories/tag.repository.ts @@ -4,10 +4,8 @@ import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { TagEntity } from 'src/entities/tag.entity'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AssetTagItem, ITagRepository } from 'src/interfaces/tag.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { DataSource, In, Repository, TreeRepository } from 'typeorm'; -@Instrumentation() @Injectable() export class TagRepository implements ITagRepository { constructor( diff --git a/server/src/repositories/telemetry.repository.ts b/server/src/repositories/telemetry.repository.ts new file mode 100644 index 0000000000..f450c162dc --- /dev/null +++ b/server/src/repositories/telemetry.repository.ts @@ -0,0 +1,166 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { MetricOptions } from '@opentelemetry/api'; +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; +import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'; +import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; +import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis'; +import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core'; +import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'; +import { NodeSDK, contextBase, metrics, resources } from '@opentelemetry/sdk-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { ClassConstructor } from 'class-transformer'; +import { snakeCase, startCase } from 'lodash'; +import { MetricService } from 'nestjs-otel'; +import { copyMetadataFromFunctionToFunction } from 'nestjs-otel/lib/opentelemetry.utils'; +import { serverVersion } from 'src/constants'; +import { MetadataKey } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { IMetricGroupRepository, ITelemetryRepository, MetricGroupOptions } from 'src/interfaces/telemetry.interface'; + +class MetricGroupRepository implements IMetricGroupRepository { + private enabled = false; + + constructor(private metricService: MetricService) {} + + addToCounter(name: string, value: number, options?: MetricOptions): void { + if (this.enabled) { + this.metricService.getCounter(name, options).add(value); + } + } + + addToGauge(name: string, value: number, options?: MetricOptions): void { + if (this.enabled) { + this.metricService.getUpDownCounter(name, options).add(value); + } + } + + addToHistogram(name: string, value: number, options?: MetricOptions): void { + if (this.enabled) { + this.metricService.getHistogram(name, options).record(value); + } + } + + configure(options: MetricGroupOptions): this { + this.enabled = options.enabled; + return this; + } +} + +const aggregation = new metrics.ExplicitBucketHistogramAggregation( + [0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10_000], + true, +); + +let instance: NodeSDK | undefined; + +export const bootstrapTelemetry = (port: number) => { + if (instance) { + throw new Error('OpenTelemetry SDK already started'); + } + instance = new NodeSDK({ + resource: new resources.Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: `immich`, + [SemanticResourceAttributes.SERVICE_VERSION]: serverVersion.toString(), + }), + metricReader: new PrometheusExporter({ port }), + contextManager: new AsyncLocalStorageContextManager(), + instrumentations: [ + new HttpInstrumentation(), + new IORedisInstrumentation(), + new NestInstrumentation(), + new PgInstrumentation(), + ], + views: [new metrics.View({ aggregation, instrumentName: '*', instrumentUnit: 'ms' })], + }); + + instance.start(); +}; + +export const teardownTelemetry = async () => { + if (instance) { + await instance.shutdown(); + instance = undefined; + } +}; + +@Injectable() +export class TelemetryRepository implements ITelemetryRepository { + api: MetricGroupRepository; + host: MetricGroupRepository; + jobs: MetricGroupRepository; + repo: MetricGroupRepository; + + constructor( + private metricService: MetricService, + private reflect: Reflector, + @Inject(IConfigRepository) private configRepository: IConfigRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, + ) { + const { telemetry } = this.configRepository.getEnv(); + const { apiMetrics, hostMetrics, jobMetrics, repoMetrics } = telemetry; + + this.api = new MetricGroupRepository(metricService).configure({ enabled: apiMetrics }); + this.host = new MetricGroupRepository(metricService).configure({ enabled: hostMetrics }); + this.jobs = new MetricGroupRepository(metricService).configure({ enabled: jobMetrics }); + this.repo = new MetricGroupRepository(metricService).configure({ enabled: repoMetrics }); + } + + setup({ repositories }: { repositories: ClassConstructor[] }) { + const { telemetry } = this.configRepository.getEnv(); + if (!telemetry.enabled || !telemetry.repoMetrics) { + return; + } + + for (const Repository of repositories) { + const isEnabled = this.reflect.get(MetadataKey.TELEMETRY_ENABLED, Repository) ?? true; + if (!isEnabled) { + this.logger.debug(`Telemetry disabled for ${Repository.name}`); + continue; + } + + this.wrap(Repository); + } + } + + private wrap(Repository: ClassConstructor) { + const className = Repository.name; + const descriptors = Object.getOwnPropertyDescriptors(Repository.prototype); + const unit = 'ms'; + + for (const [propName, descriptor] of Object.entries(descriptors)) { + const isMethod = typeof descriptor.value == 'function' && propName !== 'constructor'; + if (!isMethod) { + continue; + } + + const method = descriptor.value; + const propertyName = snakeCase(String(propName)); + const metricName = `${snakeCase(className).replaceAll(/_(?=(repository)|(controller)|(provider)|(service)|(module))/g, '.')}.${propertyName}.duration`; + + const histogram = this.metricService.getHistogram(metricName, { + prefix: 'immich', + description: `The elapsed time in ${unit} for the ${startCase(className)} to ${propertyName.toLowerCase()}`, + unit, + valueType: contextBase.ValueType.DOUBLE, + }); + + descriptor.value = function (...args: any[]) { + const start = performance.now(); + const result = method.apply(this, args); + + void Promise.resolve(result) + .then(() => histogram.record(performance.now() - start, {})) + .catch(() => { + // noop + }); + + return result; + }; + + copyMetadataFromFunctionToFunction(method, descriptor.value); + Object.defineProperty(Repository.prototype, propName, descriptor); + } + } +} diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index c64d5a3655..6ac8536ef8 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -10,10 +10,8 @@ import { UserListFilter, UserStatsQueryResponse, } from 'src/interfaces/user.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { IsNull, Not, Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class UserRepository implements IUserRepository { constructor( diff --git a/server/src/repositories/version-history.repository.ts b/server/src/repositories/version-history.repository.ts index 26c638bd76..e32ceaf4e9 100644 --- a/server/src/repositories/version-history.repository.ts +++ b/server/src/repositories/version-history.repository.ts @@ -2,10 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { VersionHistoryEntity } from 'src/entities/version-history.entity'; import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; -import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; -@Instrumentation() @Injectable() export class VersionHistoryRepository implements IVersionHistoryRepository { constructor(@InjectRepository(VersionHistoryEntity) private repository: Repository) {} diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts index 33c8f5dd7f..12c93ee127 100644 --- a/server/src/services/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -537,10 +537,6 @@ describe(AlbumService.name, () => { albumThumbnailAssetId: 'asset-1', }); expect(albumMock.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); - expect(eventMock.emit).toHaveBeenCalledWith('album.update', { - id: 'album-123', - updatedBy: authStub.admin.user.id, - }); }); it('should not set the thumbnail if the album has one already', async () => { @@ -583,7 +579,7 @@ describe(AlbumService.name, () => { expect(albumMock.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); expect(eventMock.emit).toHaveBeenCalledWith('album.update', { id: 'album-123', - updatedBy: authStub.user1.user.id, + recipientIds: ['admin_id'], }); }); diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index e8acce9b6c..2cf83e9b99 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -174,7 +174,13 @@ export class AlbumService extends BaseService { albumThumbnailAssetId: album.albumThumbnailAssetId ?? firstNewAssetId, }); - await this.eventRepository.emit('album.update', { id, updatedBy: auth.user.id }); + const allUsersExceptUs = [...album.albumUsers.map(({ user }) => user.id), album.owner.id].filter( + (userId) => userId !== auth.user.id, + ); + + if (allUsersExceptUs.length > 0) { + await this.eventRepository.emit('album.update', { id, recipientIds: allUsersExceptUs }); + } } return results; diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 2bb717b45b..441a81cf91 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -20,7 +20,6 @@ import { IMapRepository } from 'src/interfaces/map.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMemoryRepository } from 'src/interfaces/memory.interface'; import { IMetadataRepository } from 'src/interfaces/metadata.interface'; -import { IMetricRepository } from 'src/interfaces/metric.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { INotificationRepository } from 'src/interfaces/notification.interface'; import { IOAuthRepository } from 'src/interfaces/oauth.interface'; @@ -34,6 +33,7 @@ import { IStackRepository } from 'src/interfaces/stack.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ITagRepository } from 'src/interfaces/tag.interface'; +import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; import { ITrashRepository } from 'src/interfaces/trash.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; @@ -64,7 +64,6 @@ export class BaseService { @Inject(IMediaRepository) protected mediaRepository: IMediaRepository, @Inject(IMemoryRepository) protected memoryRepository: IMemoryRepository, @Inject(IMetadataRepository) protected metadataRepository: IMetadataRepository, - @Inject(IMetricRepository) protected metricRepository: IMetricRepository, @Inject(IMoveRepository) protected moveRepository: IMoveRepository, @Inject(INotificationRepository) protected notificationRepository: INotificationRepository, @Inject(IOAuthRepository) protected oauthRepository: IOAuthRepository, @@ -78,6 +77,7 @@ export class BaseService { @Inject(IStorageRepository) protected storageRepository: IStorageRepository, @Inject(ISystemMetadataRepository) protected systemMetadataRepository: ISystemMetadataRepository, @Inject(ITagRepository) protected tagRepository: ITagRepository, + @Inject(ITelemetryRepository) protected telemetryRepository: ITelemetryRepository, @Inject(ITrashRepository) protected trashRepository: ITrashRepository, @Inject(IUserRepository) protected userRepository: IUserRepository, @Inject(IVersionHistoryRepository) protected versionRepository: IVersionHistoryRepository, diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index 971509447f..46771ff046 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -124,7 +124,7 @@ export class JobService extends BaseService { throw new BadRequestException(`Job is already running`); } - this.metricRepository.jobs.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1); + this.telemetryRepository.jobs.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1); switch (name) { case QueueName.VIDEO_CONVERSION: { @@ -197,19 +197,19 @@ export class JobService extends BaseService { } const queueMetric = `immich.queues.${snakeCase(queueName)}.active`; - this.metricRepository.jobs.addToGauge(queueMetric, 1); + this.telemetryRepository.jobs.addToGauge(queueMetric, 1); try { const status = await handler(data); const jobMetric = `immich.jobs.${name.replaceAll('-', '_')}.${status}`; - this.metricRepository.jobs.addToCounter(jobMetric, 1); + this.telemetryRepository.jobs.addToCounter(jobMetric, 1); if (status === JobStatus.SUCCESS || status == JobStatus.SKIPPED) { await this.onDone(item); } } catch (error: Error | any) { this.logger.error(`Unable to run job handler (${queueName}/${name}): ${error}`, error?.stack, data); } finally { - this.metricRepository.jobs.addToGauge(queueMetric, -1); + this.telemetryRepository.jobs.addToGauge(queueMetric, -1); } }); } diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index b021eedbe9..5258c8d035 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -907,7 +907,9 @@ describe(LibraryService.name, () => { storageMock.stat.mockResolvedValue({ isDirectory: () => true } as Stats); storageMock.checkFileExists.mockResolvedValue(true); - await expect(sut.update('library-id', { importPaths: ['foo/bar'] })).resolves.toEqual( + const cwd = process.cwd(); + + await expect(sut.update('library-id', { importPaths: [`${cwd}/foo/bar`] })).resolves.toEqual( mapLibrary(libraryStub.externalLibrary1), ); expect(libraryMock.update).toHaveBeenCalledWith(expect.objectContaining({ id: 'library-id' })); @@ -1300,14 +1302,31 @@ describe(LibraryService.name, () => { }); }); + it('should detect when import path is not absolute', async () => { + const cwd = process.cwd(); + + await expect(sut.validate('library-id', { importPaths: ['relative/path'] })).resolves.toEqual({ + importPaths: [ + { + importPath: 'relative/path', + isValid: false, + message: `Import path must be absolute, try ${cwd}/relative/path`, + }, + ], + }); + }); + it('should detect when import path is in immich media folder', async () => { storageMock.stat.mockResolvedValue({ isDirectory: () => true } as Stats); - const validImport = libraryStub.hasImmichPaths.importPaths[1]; + const cwd = process.cwd(); + + const validImport = `${cwd}/${libraryStub.hasImmichPaths.importPaths[1]}`; storageMock.checkFileExists.mockImplementation((importPath) => Promise.resolve(importPath === validImport)); - await expect( - sut.validate('library-id', { importPaths: libraryStub.hasImmichPaths.importPaths }), - ).resolves.toEqual({ + const pathStubs = libraryStub.hasImmichPaths.importPaths; + const importPaths = [pathStubs[0], validImport, pathStubs[2]]; + + await expect(sut.validate('library-id', { importPaths })).resolves.toEqual({ importPaths: [ { importPath: libraryStub.hasImmichPaths.importPaths[0], diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index a75403326d..e319983d3b 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -1,6 +1,6 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { R_OK } from 'node:constants'; -import path, { basename, parse } from 'node:path'; +import path, { basename, isAbsolute, parse } from 'node:path'; import picomatch from 'picomatch'; import { StorageCore } from 'src/cores/storage.core'; import { OnEvent } from 'src/decorators'; @@ -268,6 +268,11 @@ export class LibraryService extends BaseService { return validation; } + if (!isAbsolute(importPath)) { + validation.message = `Import path must be absolute, try ${path.resolve(importPath)}`; + return validation; + } + try { const stat = await this.storageRepository.stat(importPath); if (!stat.isDirectory()) { diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 0489169c1a..afd21ee9e9 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1327,7 +1327,6 @@ describe(MediaService.name, () => { '-fps_mode passthrough', '-map 0:0', '-map 0:1', - '-strict unofficial', '-v verbose', '-vf scale=-2:720,format=yuv420p', '-preset 12', @@ -1453,7 +1452,6 @@ describe(MediaService.name, () => { '-fps_mode passthrough', '-map 0:0', '-map 0:1', - '-strict unofficial', '-g 256', '-v verbose', '-vf format=nv12,hwupload_cuda,scale_cuda=-2:720', @@ -1614,7 +1612,6 @@ describe(MediaService.name, () => { '-fps_mode passthrough', '-map 0:0', '-map 0:1', - '-strict unofficial', '-bf 7', '-refs 5', '-g 256', @@ -1800,7 +1797,6 @@ describe(MediaService.name, () => { '-fps_mode passthrough', '-map 0:0', '-map 0:1', - '-strict unofficial', '-g 256', '-v verbose', '-vf format=nv12,hwupload,scale_vaapi=-2:720:mode=hq:out_range=pc', @@ -2071,7 +2067,6 @@ describe(MediaService.name, () => { '-fps_mode passthrough', '-map 0:0', '-map 0:1', - '-strict unofficial', '-g 256', '-v verbose', '-vf scale_rkrga=-2:720:format=nv12:afbc=1', @@ -2292,6 +2287,22 @@ describe(MediaService.name, () => { expect(mediaMock.probe).toHaveBeenCalledWith(assetStub.video.originalPath, { countFrames: false }); }); + + it('should process unknown audio stream', async () => { + mediaMock.probe.mockResolvedValue(probeStub.audioStreamUnknown); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.objectContaining({ + inputOptions: expect.any(Array), + outputOptions: expect.arrayContaining(['-c:a copy']), + twoPass: false, + }), + ); + }); }); describe('isSRGB', () => { diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index f270e21b6f..8393f5dc76 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -147,10 +147,10 @@ export class MediaService extends BaseService { } let generated: { previewPath: string; thumbnailPath: string; thumbhash: Buffer }; - if (asset.type === AssetType.IMAGE) { - generated = await this.generateImageThumbnails(asset); - } else if (asset.type === AssetType.VIDEO) { + if (asset.type === AssetType.VIDEO || asset.originalFileName.toLowerCase().endsWith('.gif')) { generated = await this.generateVideoThumbnails(asset); + } else if (asset.type === AssetType.IMAGE) { + generated = await this.generateImageThumbnails(asset); } else { this.logger.warn(`Skipping thumbnail generation for asset ${id}: ${asset.type} is not an image or video`); return JobStatus.SKIPPED; @@ -349,7 +349,9 @@ export class MediaService extends BaseService { } private getMainStream(streams: T[]): T { - return streams.sort((stream1, stream2) => stream2.frameCount - stream1.frameCount)[0]; + return streams + .filter((stream) => stream.codecName !== 'unknown') + .sort((stream1, stream2) => stream2.frameCount - stream1.frameCount)[0]; } private getTranscodeTarget( diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 38c86bcdb1..a45bcd4252 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -579,7 +579,7 @@ export class MetadataService extends BaseService { private getDates(asset: AssetEntity, exifTags: ImmichTags) { const dateTime = firstDateTime(exifTags as Maybe, EXIF_DATE_TAGS); - this.logger.debug(`Asset ${asset.id} date time is ${dateTime}`); + this.logger.verbose(`Asset ${asset.id} date time is ${dateTime}`); // timezone let timeZone = exifTags.tz ?? null; @@ -590,7 +590,7 @@ export class MetadataService extends BaseService { } if (timeZone) { - this.logger.debug(`Asset ${asset.id} timezone is ${timeZone} (via ${exifTags.tzSource})`); + this.logger.verbose(`Asset ${asset.id} timezone is ${timeZone} (via ${exifTags.tzSource})`); } else { this.logger.warn(`Asset ${asset.id} has no time zone information`); } @@ -603,7 +603,7 @@ export class MetadataService extends BaseService { localDateTime = asset.fileCreatedAt; } - this.logger.debug(`Asset ${asset.id} has a local time of ${localDateTime.toISOString()}`); + this.logger.verbose(`Asset ${asset.id} has a local time of ${localDateTime.toISOString()}`); let modifyDate = asset.fileModifiedAt; try { diff --git a/server/src/services/microservices.service.ts b/server/src/services/microservices.service.ts index d1d2bb8f20..275103d05c 100644 --- a/server/src/services/microservices.service.ts +++ b/server/src/services/microservices.service.ts @@ -20,7 +20,6 @@ import { TagService } from 'src/services/tag.service'; import { TrashService } from 'src/services/trash.service'; import { UserService } from 'src/services/user.service'; import { VersionService } from 'src/services/version.service'; -import { otelShutdown } from 'src/utils/instrumentation'; @Injectable() export class MicroservicesService { @@ -101,8 +100,4 @@ export class MicroservicesService { [JobName.QUEUE_TRASH_EMPTY]: () => this.trashService.handleQueueEmptyTrash(), }); } - - async onShutdown() { - await otelShutdown(); - } } diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index 028e512b39..d07d06443a 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -7,7 +7,7 @@ import { AssetFileType, UserMetadataKey } from 'src/enum'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; -import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; +import { IJobRepository, INotifyAlbumUpdateJob, JobName, JobStatus } from 'src/interfaces/job.interface'; import { EmailTemplate, INotificationRepository } from 'src/interfaces/notification.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; @@ -170,10 +170,10 @@ describe(NotificationService.name, () => { describe('onAlbumUpdateEvent', () => { it('should queue notify album update event', async () => { - await sut.onAlbumUpdate({ id: '', updatedBy: '42' }); + await sut.onAlbumUpdate({ id: 'album', recipientIds: ['42'] }); expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.NOTIFY_ALBUM_UPDATE, - data: { id: '', senderId: '42' }, + data: { id: 'album', recipientIds: ['42'], delay: 300_000 }, }); }); }); @@ -512,34 +512,17 @@ describe(NotificationService.name, () => { describe('handleAlbumUpdate', () => { it('should skip if album could not be found', async () => { - await expect(sut.handleAlbumUpdate({ id: '', senderId: '' })).resolves.toBe(JobStatus.SKIPPED); + await expect(sut.handleAlbumUpdate({ id: '', recipientIds: ['1'] })).resolves.toBe(JobStatus.SKIPPED); expect(userMock.get).not.toHaveBeenCalled(); }); it('should skip if owner could not be found', async () => { albumMock.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail); - await expect(sut.handleAlbumUpdate({ id: '', senderId: '' })).resolves.toBe(JobStatus.SKIPPED); + await expect(sut.handleAlbumUpdate({ id: '', recipientIds: ['1'] })).resolves.toBe(JobStatus.SKIPPED); expect(systemMock.get).not.toHaveBeenCalled(); }); - it('should filter out the sender', async () => { - albumMock.getById.mockResolvedValue({ - ...albumStub.emptyWithValidThumbnail, - albumUsers: [ - { user: { id: userStub.user1.id } } as AlbumUserEntity, - { user: { id: userStub.user2.id } } as AlbumUserEntity, - ], - }); - userMock.get.mockResolvedValue(userStub.user1); - notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' }); - - await sut.handleAlbumUpdate({ id: '', senderId: userStub.user1.id }); - expect(userMock.get).not.toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(userMock.get).toHaveBeenCalledWith(userStub.user2.id, { withDeleted: false }); - expect(notificationMock.renderEmail).toHaveBeenCalledOnce(); - }); - it('should skip recipient that could not be looked up', async () => { albumMock.getById.mockResolvedValue({ ...albumStub.emptyWithValidThumbnail, @@ -548,7 +531,7 @@ describe(NotificationService.name, () => { userMock.get.mockResolvedValueOnce(userStub.user1); notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' }); - await sut.handleAlbumUpdate({ id: '', senderId: '' }); + await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); expect(userMock.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); expect(notificationMock.renderEmail).not.toHaveBeenCalled(); }); @@ -571,7 +554,7 @@ describe(NotificationService.name, () => { }); notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' }); - await sut.handleAlbumUpdate({ id: '', senderId: '' }); + await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); expect(userMock.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); expect(notificationMock.renderEmail).not.toHaveBeenCalled(); }); @@ -594,7 +577,7 @@ describe(NotificationService.name, () => { }); notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' }); - await sut.handleAlbumUpdate({ id: '', senderId: '' }); + await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); expect(userMock.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); expect(notificationMock.renderEmail).not.toHaveBeenCalled(); }); @@ -607,11 +590,24 @@ describe(NotificationService.name, () => { userMock.get.mockResolvedValue(userStub.user1); notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' }); - await sut.handleAlbumUpdate({ id: '', senderId: '' }); + await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); expect(userMock.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); expect(notificationMock.renderEmail).toHaveBeenCalled(); expect(jobMock.queue).toHaveBeenCalled(); }); + + it('should add new recipients for new images if job is already queued', async () => { + jobMock.removeJob.mockResolvedValue({ id: '1', recipientIds: ['2', '3', '4'] } as INotifyAlbumUpdateJob); + await sut.onAlbumUpdate({ id: '1', recipientIds: ['1', '2', '3'] } as INotifyAlbumUpdateJob); + expect(jobMock.queue).toHaveBeenCalledWith({ + name: JobName.NOTIFY_ALBUM_UPDATE, + data: { + id: '1', + delay: 300_000, + recipientIds: ['1', '2', '3', '4'], + }, + }); + }); }); describe('handleSendEmail', () => { diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index 122a09ee2e..c3c7727468 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -5,9 +5,11 @@ import { AlbumEntity } from 'src/entities/album.entity'; import { ArgOf } from 'src/interfaces/event.interface'; import { IEmailJob, + IEntityJob, INotifyAlbumInviteJob, INotifyAlbumUpdateJob, INotifySignupJob, + JobItem, JobName, JobStatus, } from 'src/interfaces/job.interface'; @@ -21,6 +23,8 @@ import { getPreferences } from 'src/utils/preferences'; @Injectable() export class NotificationService extends BaseService { + private static albumUpdateEmailDelayMs = 300_000; + @OnEvent({ name: 'config.update' }) onConfigUpdate({ oldConfig, newConfig }: ArgOf<'config.update'>) { this.eventRepository.clientBroadcast('on_config_update'); @@ -100,8 +104,30 @@ export class NotificationService extends BaseService { } @OnEvent({ name: 'album.update' }) - async onAlbumUpdate({ id, updatedBy }: ArgOf<'album.update'>) { - await this.jobRepository.queue({ name: JobName.NOTIFY_ALBUM_UPDATE, data: { id, senderId: updatedBy } }); + async onAlbumUpdate({ id, recipientIds }: ArgOf<'album.update'>) { + // if recipientIds is empty, album likely only has one user part of it, don't queue notification if so + if (recipientIds.length === 0) { + return; + } + + const job: JobItem = { + name: JobName.NOTIFY_ALBUM_UPDATE, + data: { id, recipientIds, delay: NotificationService.albumUpdateEmailDelayMs }, + }; + + const previousJobData = await this.jobRepository.removeJob(id, JobName.NOTIFY_ALBUM_UPDATE); + if (previousJobData && this.isAlbumUpdateJob(previousJobData)) { + for (const id of previousJobData.recipientIds) { + if (!recipientIds.includes(id)) { + recipientIds.push(id); + } + } + } + await this.jobRepository.queue(job); + } + + private isAlbumUpdateJob(job: IEntityJob): job is INotifyAlbumUpdateJob { + return 'recipientIds' in job; } @OnEvent({ name: 'album.invite' }) @@ -228,7 +254,7 @@ export class NotificationService extends BaseService { return JobStatus.SUCCESS; } - async handleAlbumUpdate({ id, senderId }: INotifyAlbumUpdateJob) { + async handleAlbumUpdate({ id, recipientIds }: INotifyAlbumUpdateJob) { const album = await this.albumRepository.getById(id, { withAssets: false }); if (!album) { @@ -240,7 +266,9 @@ export class NotificationService extends BaseService { return JobStatus.SKIPPED; } - const recipients = [...album.albumUsers.map((user) => user.user), owner].filter((user) => user.id !== senderId); + const recipients = [...album.albumUsers.map((user) => user.user), owner].filter((user) => + recipientIds.includes(user.id), + ); const attachment = await this.getAlbumThumbnailAttachment(album); const { server } = await this.getConfig({ withCache: false }); diff --git a/server/src/utils/instrumentation.ts b/server/src/utils/instrumentation.ts deleted file mode 100644 index bd522f27b2..0000000000 --- a/server/src/utils/instrumentation.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; -import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'; -import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; -import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis'; -import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core'; -import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'; -import { NodeSDK, contextBase, metrics, resources } from '@opentelemetry/sdk-node'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { snakeCase, startCase } from 'lodash'; -import { copyMetadataFromFunctionToFunction } from 'nestjs-otel/lib/opentelemetry.utils'; -import { performance } from 'node:perf_hooks'; -import { serverVersion } from 'src/constants'; -import { DecorateAll } from 'src/decorators'; -import { ConfigRepository } from 'src/repositories/config.repository'; - -const aggregation = new metrics.ExplicitBucketHistogramAggregation( - [0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10_000], - true, -); - -const { telemetry } = new ConfigRepository().getEnv(); - -let otelSingleton: NodeSDK | undefined; - -export const otelStart = (port: number) => { - if (otelSingleton) { - throw new Error('OpenTelemetry SDK already started'); - } - otelSingleton = new NodeSDK({ - resource: new resources.Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: `immich`, - [SemanticResourceAttributes.SERVICE_VERSION]: serverVersion.toString(), - }), - metricReader: new PrometheusExporter({ port }), - contextManager: new AsyncLocalStorageContextManager(), - instrumentations: [ - new HttpInstrumentation(), - new IORedisInstrumentation(), - new NestInstrumentation(), - new PgInstrumentation(), - ], - views: [new metrics.View({ aggregation, instrumentName: '*', instrumentUnit: 'ms' })], - }); - otelSingleton.start(); -}; - -export const otelShutdown = async () => { - if (otelSingleton) { - await otelSingleton.shutdown(); - otelSingleton = undefined; - } -}; - -function ExecutionTimeHistogram({ - description, - unit = 'ms', - valueType = contextBase.ValueType.DOUBLE, -}: contextBase.MetricOptions = {}) { - return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { - if (!telemetry.repoMetrics || process.env.OTEL_SDK_DISABLED) { - return; - } - - const method = descriptor.value; - const className = target.constructor.name as string; - const propertyName = String(propertyKey); - const metricName = `${snakeCase(className).replaceAll(/_(?=(repository)|(controller)|(provider)|(service)|(module))/g, '.')}.${snakeCase(propertyName)}.duration`; - - const metricDescription = - description ?? - `The elapsed time in ${unit} for the ${startCase(className)} to ${startCase(propertyName).toLowerCase()}`; - - let histogram: contextBase.Histogram | undefined; - - descriptor.value = function (...args: any[]) { - const start = performance.now(); - const result = method.apply(this, args); - - void Promise.resolve(result) - .then(() => { - const end = performance.now(); - if (!histogram) { - histogram = contextBase.metrics - .getMeter('immich') - .createHistogram(metricName, { description: metricDescription, unit, valueType }); - } - histogram.record(end - start, {}); - }) - .catch(() => { - // noop - }); - - return result; - }; - - copyMetadataFromFunctionToFunction(method, descriptor.value); - }; -} - -export const Instrumentation = () => DecorateAll(ExecutionTimeHistogram()); diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index 55f92d109a..03d57296d8 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -118,7 +118,6 @@ export class BaseConfig implements VideoCodecSWConfig { '-fps_mode passthrough', // explicitly selects the video stream instead of leaving it up to FFmpeg `-map 0:${videoStream.index}`, - '-strict unofficial', ]; if (audioStream) { diff --git a/server/src/workers/api.ts b/server/src/workers/api.ts index b369b56953..6451f1b792 100644 --- a/server/src/workers/api.ts +++ b/server/src/workers/api.ts @@ -11,16 +11,18 @@ import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { WebSocketAdapter } from 'src/middleware/websocket.adapter'; import { ConfigRepository } from 'src/repositories/config.repository'; +import { bootstrapTelemetry } from 'src/repositories/telemetry.repository'; import { ApiService } from 'src/services/api.service'; import { isStartUpError } from 'src/services/storage.service'; -import { otelStart } from 'src/utils/instrumentation'; import { useSwagger } from 'src/utils/misc'; async function bootstrap() { process.title = 'immich-api'; const { telemetry, network } = new ConfigRepository().getEnv(); - otelStart(telemetry.apiPort); + if (telemetry.enabled) { + bootstrapTelemetry(telemetry.apiPort); + } const app = await NestFactory.create(ApiModule, { bufferLogs: true }); const logger = await app.resolve(ILoggerRepository); diff --git a/server/src/workers/microservices.ts b/server/src/workers/microservices.ts index 7b60fb8db6..df4abb01da 100644 --- a/server/src/workers/microservices.ts +++ b/server/src/workers/microservices.ts @@ -6,12 +6,14 @@ import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { WebSocketAdapter } from 'src/middleware/websocket.adapter'; import { ConfigRepository } from 'src/repositories/config.repository'; +import { bootstrapTelemetry } from 'src/repositories/telemetry.repository'; import { isStartUpError } from 'src/services/storage.service'; -import { otelStart } from 'src/utils/instrumentation'; export async function bootstrap() { const { telemetry } = new ConfigRepository().getEnv(); - otelStart(telemetry.microservicesPort); + if (telemetry.enabled) { + bootstrapTelemetry(telemetry.microservicesPort); + } const app = await NestFactory.create(MicroservicesModule, { bufferLogs: true }); const logger = await app.resolve(ILoggerRepository); diff --git a/server/start.sh b/server/start.sh index 518d9229a8..1a08d01a75 100755 --- a/server/start.sh +++ b/server/start.sh @@ -4,6 +4,7 @@ echo "Initializing Immich $IMMICH_SOURCE_REF" lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" export LD_PRELOAD="$lib_path" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" read_file_and_export() { if [ -n "${!1}" ]; then diff --git a/server/test/fixtures/library.stub.ts b/server/test/fixtures/library.stub.ts index b2e132da3e..bb40035dcc 100644 --- a/server/test/fixtures/library.stub.ts +++ b/server/test/fixtures/library.stub.ts @@ -68,7 +68,7 @@ export const libraryStub = { assets: [], owner: userStub.admin, ownerId: 'user-id', - importPaths: ['upload/thumbs', '/xyz', 'upload/library'], + importPaths: ['upload/thumbs', 'xyz', 'upload/library'], createdAt: new Date('2023-01-01'), updatedAt: new Date('2023-01-01'), refreshedAt: null, diff --git a/server/test/fixtures/media.stub.ts b/server/test/fixtures/media.stub.ts index 9b4e15a95d..cdcdfd4d5e 100644 --- a/server/test/fixtures/media.stub.ts +++ b/server/test/fixtures/media.stub.ts @@ -154,6 +154,13 @@ export const probeStub = { ...probeStubDefault, audioStreams: [{ index: 1, codecName: 'aac', frameCount: 100 }], }), + audioStreamUnknown: Object.freeze({ + ...probeStubDefault, + audioStreams: [ + { index: 0, codecName: 'aac', frameCount: 100 }, + { index: 1, codecName: 'unknown', frameCount: 200 }, + ], + }), matroskaContainer: Object.freeze({ ...probeStubDefault, format: { diff --git a/server/test/fixtures/user.stub.ts b/server/test/fixtures/user.stub.ts index b65cd6b395..9553b5344a 100644 --- a/server/test/fixtures/user.stub.ts +++ b/server/test/fixtures/user.stub.ts @@ -7,6 +7,7 @@ export const userStub = { ...authStub.admin.user, password: 'admin_password', name: 'admin_name', + id: 'admin_id', storageLabel: 'admin', oauthId: '', shouldChangePassword: false, diff --git a/server/test/repositories/job.repository.mock.ts b/server/test/repositories/job.repository.mock.ts index 871801830a..cfa1826dd8 100644 --- a/server/test/repositories/job.repository.mock.ts +++ b/server/test/repositories/job.repository.mock.ts @@ -16,5 +16,6 @@ export const newJobRepositoryMock = (): Mocked => { getJobCounts: vitest.fn(), clear: vitest.fn(), waitForQueueCompletion: vitest.fn(), + removeJob: vitest.fn(), }; }; diff --git a/server/test/repositories/metric.repository.mock.ts b/server/test/repositories/metric.repository.mock.ts deleted file mode 100644 index e2c3e2aac1..0000000000 --- a/server/test/repositories/metric.repository.mock.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { IMetricRepository } from 'src/interfaces/metric.interface'; -import { Mocked, vitest } from 'vitest'; - -export const newMetricRepositoryMock = (): Mocked => { - return { - api: { - addToCounter: vitest.fn(), - addToGauge: vitest.fn(), - addToHistogram: vitest.fn(), - configure: vitest.fn(), - }, - host: { - addToCounter: vitest.fn(), - addToGauge: vitest.fn(), - addToHistogram: vitest.fn(), - configure: vitest.fn(), - }, - jobs: { - addToCounter: vitest.fn(), - addToGauge: vitest.fn(), - addToHistogram: vitest.fn(), - configure: vitest.fn(), - }, - repo: { - addToCounter: vitest.fn(), - addToGauge: vitest.fn(), - addToHistogram: vitest.fn(), - configure: vitest.fn(), - }, - }; -}; diff --git a/server/test/repositories/telemetry.repository.mock.ts b/server/test/repositories/telemetry.repository.mock.ts new file mode 100644 index 0000000000..2d537e888a --- /dev/null +++ b/server/test/repositories/telemetry.repository.mock.ts @@ -0,0 +1,21 @@ +import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; +import { Mocked, vitest } from 'vitest'; + +const newMetricGroupMock = () => { + return { + addToCounter: vitest.fn(), + addToGauge: vitest.fn(), + addToHistogram: vitest.fn(), + configure: vitest.fn(), + }; +}; + +export const newTelemetryRepositoryMock = (): Mocked => { + return { + setup: vitest.fn(), + api: newMetricGroupMock(), + host: newMetricGroupMock(), + jobs: newMetricGroupMock(), + repo: newMetricGroupMock(), + }; +}; diff --git a/server/test/utils.ts b/server/test/utils.ts index 3b7e80994d..9a40a22c2c 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -20,7 +20,6 @@ import { newMapRepositoryMock } from 'test/repositories/map.repository.mock'; import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock'; import { newMemoryRepositoryMock } from 'test/repositories/memory.repository.mock'; import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository.mock'; -import { newMetricRepositoryMock } from 'test/repositories/metric.repository.mock'; import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock'; import { newNotificationRepositoryMock } from 'test/repositories/notification.repository.mock'; import { newOAuthRepositoryMock } from 'test/repositories/oauth.repository.mock'; @@ -34,6 +33,7 @@ import { newStackRepositoryMock } from 'test/repositories/stack.repository.mock' import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; import { newTagRepositoryMock } from 'test/repositories/tag.repository.mock'; +import { newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; import { newTrashRepositoryMock } from 'test/repositories/trash.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; import { newVersionHistoryRepositoryMock } from 'test/repositories/version-history.repository.mock'; @@ -73,7 +73,6 @@ export const newTestService = ( const mediaMock = newMediaRepositoryMock(); const memoryMock = newMemoryRepositoryMock(); const metadataMock = (metadataRepository || newMetadataRepositoryMock()) as Mocked; - const metricMock = newMetricRepositoryMock(); const moveMock = newMoveRepositoryMock(); const notificationMock = newNotificationRepositoryMock(); const oauthMock = newOAuthRepositoryMock(); @@ -87,6 +86,7 @@ export const newTestService = ( const storageMock = newStorageRepositoryMock(); const systemMock = newSystemMetadataRepositoryMock(); const tagMock = newTagRepositoryMock(); + const telemetryMock = newTelemetryRepositoryMock(); const trashMock = newTrashRepositoryMock(); const userMock = newUserRepositoryMock(); const versionHistoryMock = newVersionHistoryRepositoryMock(); @@ -112,7 +112,6 @@ export const newTestService = ( mediaMock, memoryMock, metadataMock, - metricMock, moveMock, notificationMock, oauthMock, @@ -126,6 +125,7 @@ export const newTestService = ( storageMock, systemMock, tagMock, + telemetryMock, trashMock, userMock, versionHistoryMock, @@ -153,7 +153,6 @@ export const newTestService = ( mediaMock, memoryMock, metadataMock, - metricMock, moveMock, notificationMock, oauthMock, @@ -167,6 +166,7 @@ export const newTestService = ( storageMock, systemMock, tagMock, + telemetryMock, trashMock, userMock, versionHistoryMock, diff --git a/web/package-lock.json b/web/package-lock.json index b91c459093..45d1198cc7 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -80,7 +80,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "typescript": "^5.3.3" } }, @@ -786,51 +786,53 @@ } }, "node_modules/@formatjs/ecma402-abstract": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.1.0.tgz", - "integrity": "sha512-SE2V2PE03K9U/YQZ3nxEOysRkQ/CfSwLHR789Uk9N0PTiWT6I+17UTDI97zYEwC1mbnjefqmtjbL8nunjPwGjw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.0.tgz", + "integrity": "sha512-IpM+ev1E4QLtstniOE29W1rqH9eTdx5hQdNL8pzrflMj/gogfaoONZqL83LUeQScHAvyMbpqP5C9MzNf+fFwhQ==", "license": "MIT", "dependencies": { - "@formatjs/fast-memoize": "2.2.0", - "@formatjs/intl-localematcher": "0.5.4", - "tslib": "^2.4.0" + "@formatjs/fast-memoize": "2.2.1", + "@formatjs/intl-localematcher": "0.5.5", + "tslib": "^2.7.0" } }, "node_modules/@formatjs/fast-memoize": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", - "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.1.tgz", + "integrity": "sha512-XS2RcOSyWxmUB7BUjj3mlPH0exsUzlf6QfhhijgI941WaJhVxXQ6mEWkdUFIdnKi3TuTYxRdelsgv3mjieIGIA==", + "license": "MIT", "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.7.0" } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.7.9", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.9.tgz", - "integrity": "sha512-9Z5buDRMsTbplXknvRlDmnpWhZrayNVcVvkH0+SSz8Ll4XD/7Tcn8m1IjxM3iBJSwQbxwxb7/g0Fkx3d4j2osw==", + "version": "2.7.10", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.10.tgz", + "integrity": "sha512-wlQfqCZ7PURkUNL2+8VTEFavPovtADU/isSKLFvDbdFmV7QPZIYqFMkhklaDYgMyLSBJa/h2MVQ2aFvoEJhxgg==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.1.0", - "@formatjs/icu-skeleton-parser": "1.8.3", - "tslib": "^2.4.0" + "@formatjs/ecma402-abstract": "2.2.0", + "@formatjs/icu-skeleton-parser": "1.8.4", + "tslib": "^2.7.0" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.3.tgz", - "integrity": "sha512-TsKAP013ayZFbWWR2KWy+f9QVZh0yDFTPK3yE4OqU2gnzafvmKTodRtJLVpfZmpXWJ5y7BWD1AsyT14mcbLzig==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.4.tgz", + "integrity": "sha512-LMQ1+Wk1QSzU4zpd5aSu7+w5oeYhupRwZnMQckLPRYhSjf2/8JWQ882BauY9NyHxs5igpuQIXZDgfkaH3PoATg==", "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "2.1.0", - "tslib": "^2.4.0" + "@formatjs/ecma402-abstract": "2.2.0", + "tslib": "^2.7.0" } }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", - "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.5.tgz", + "integrity": "sha512-t5tOGMgZ/i5+ALl2/offNqAQq/lfUnKLEw0mXQI4N4bqpedhrSE+fyKLpwnd22sK0dif6AV+ufQcTsKShB9J1g==", + "license": "MIT", "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.7.0" } }, "node_modules/@humanfs/core": { @@ -1633,30 +1635,30 @@ } }, "node_modules/@photo-sphere-viewer/core": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.10.1.tgz", - "integrity": "sha512-xdvPbfQqLl8tggqNDMcczbQGNQHbA4N9u3RCuzYzhrN2PBE2ihL5TsH85smkH4GcRrJKypSzwXF7rDrQhcs7aQ==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.11.0.tgz", + "integrity": "sha512-SjEVBBMNj5v3aSLglpTd88q+cyDw0Lke7mK3kN3p+KsQK8X0OG7GsbtI7sGj81zSCbHmV3c0DgUaXMv+xdGFaw==", "license": "MIT", "dependencies": { - "three": "^0.168.0" + "three": "^0.169.0" } }, "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.10.1.tgz", - "integrity": "sha512-159vPvsqPJ2prxnWpRH8QSaT+QlCOIac8XmhmkfwBoMqTZ8B1P+JWyuKYaDpqz4Bk/K+kncVBMNVvdro6bIccw==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.11.0.tgz", + "integrity": "sha512-xs5+vT5jYjBtEsguuJe6CVJNGxtwopb3+Zs4z/VJg0oNm2mTZF2TQu2RkSlRJZTE6a/IfZ7Zv1fJcN3Yiv7AVg==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.10.1" + "@photo-sphere-viewer/core": "5.11.0" } }, "node_modules/@photo-sphere-viewer/video-plugin": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.10.1.tgz", - "integrity": "sha512-rIoXvHuuB+qg8I0wqbe4S3YUXiliN4kTeV5Xx70dyPFtroGVEKpbvBZ8ygy029np0xALMkeKvi6cYiB40Lj1Pw==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.11.0.tgz", + "integrity": "sha512-6ApAKvwDRgVZOk4N/3SJ14IFpQ6V0QDDtknXxLI5JqU1yAvBcyb7goa5XDbyTWXYUaPKH06Fz+8UruWRzCsBXw==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.10.1" + "@photo-sphere-viewer/core": "5.11.0" } }, "node_modules/@pkgjs/parseargs": { @@ -1943,14 +1945,14 @@ } }, "node_modules/@sveltejs/enhanced-img": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.3.8.tgz", - "integrity": "sha512-n66u46ZeqHltiTm0BEjWptYmCrCY0EltEEvakmC7d5o5ZejDbOvOWm914mebbRKaP2Bezv65TNCod/wqvw/0KA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.3.9.tgz", + "integrity": "sha512-hDhoIbkDAY08II/1DWeY2lGMY9nhETC96B2HTbxoI6EDqxLErBDKqnRN3QQRuMJATxPGVNhCKUkuARi4TCLtpQ==", "dev": true, "license": "MIT", "dependencies": { "magic-string": "^0.30.5", - "svelte-parse-markup": "^0.1.2", + "svelte-parse-markup": "^0.1.5", "vite-imagetools": "^7.0.1" }, "peerDependencies": { @@ -1959,9 +1961,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.6.4.tgz", - "integrity": "sha512-qfcbyWw35cy6k9sQ1GUkhuE5qj+PgPKJx3/Aa3+veooWgN0DXZXqMS2PDgpgKDXRIFj6V1KWmMZYYPOhL45lXg==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.7.1.tgz", + "integrity": "sha512-TBVnkwgYQT3EafGQK6Eyh5FlLEBlRhCmqPTwcdOs+QdnyUc3eCAxRWtXlFxIWtmk6pqv11zdng8qTpThdTogew==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1976,7 +1978,7 @@ "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", - "sirv": "^2.0.4", + "sirv": "^3.0.0", "tiny-glob": "^0.2.9" }, "bin": { @@ -2123,9 +2125,9 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", - "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.2.tgz", + "integrity": "sha512-P6GJD4yqc9jZLbe98j/EkyQDTPgqftohZF5FBkHY5BUERZmcf4HeO2k0XaefEg329ux2p21i1A1DmyQ1kKw2Jw==", "dev": true, "license": "MIT", "dependencies": { @@ -2383,17 +2385,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", + "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/type-utils": "8.10.0", + "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2417,16 +2419,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", + "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4" }, "engines": { @@ -2446,14 +2448,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", + "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2464,14 +2466,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", + "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/utils": "8.10.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2489,9 +2491,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", + "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", "dev": true, "license": "MIT", "engines": { @@ -2503,14 +2505,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", + "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2558,16 +2560,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", + "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2581,13 +2583,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", + "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/types": "8.10.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2599,9 +2601,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.2.tgz", - "integrity": "sha512-b7kHrFrs2urS0cOk5N10lttI8UdJ/yP3nB4JYTREvR5o18cR99yPpK4gK8oQgI42BVv0ILWYUSYB7AXkAUDc0g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.3.tgz", + "integrity": "sha512-2OJ3c7UPoFSmBZwqD2VEkUw6A/tzPF0LmW0ZZhhB8PFxuc+9IBG/FaSM+RLEenc7ljzFvGN+G0nGQoZnh7sy2A==", "dev": true, "license": "MIT", "dependencies": { @@ -2622,8 +2624,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.2", - "vitest": "2.1.2" + "@vitest/browser": "2.1.3", + "vitest": "2.1.3" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -2632,14 +2634,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.2.tgz", - "integrity": "sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz", + "integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -2648,13 +2650,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.2.tgz", - "integrity": "sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz", + "integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "^2.1.0-beta.1", + "@vitest/spy": "2.1.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.11" }, @@ -2662,7 +2664,7 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/spy": "2.1.2", + "@vitest/spy": "2.1.3", "msw": "^2.3.5", "vite": "^5.0.0" }, @@ -2676,9 +2678,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.2.tgz", - "integrity": "sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", + "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2689,13 +2691,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.2.tgz", - "integrity": "sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz", + "integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.2", + "@vitest/utils": "2.1.3", "pathe": "^1.1.2" }, "funding": { @@ -2703,13 +2705,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.2.tgz", - "integrity": "sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz", + "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "magic-string": "^0.30.11", "pathe": "^1.1.2" }, @@ -2718,9 +2720,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.2.tgz", - "integrity": "sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz", + "integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2731,13 +2733,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.2.tgz", - "integrity": "sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz", + "integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.2", + "@vitest/pretty-format": "2.1.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -3893,9 +3895,9 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "2.44.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.44.1.tgz", - "integrity": "sha512-w6wkoJPw1FJKFtM/2oln21rlu5+HTd2CSkkzhm32A+trNoW2EYQqTQAbDTU6k2GI/6Vh64rBHYQejqEgDld7fw==", + "version": "2.45.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.45.1.tgz", + "integrity": "sha512-mYAKNDRji0YWl7o00KQi0enREcrtzcN7xwK/8lwk5uLRoKLjzPXc+WjngsYpPV35I3AF7UlXc1+JfyNMJS+njA==", "dev": true, "license": "MIT", "dependencies": { @@ -3909,7 +3911,7 @@ "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.1.0", "semver": "^7.6.2", - "svelte-eslint-parser": "^0.41.1" + "svelte-eslint-parser": "^0.42.0" }, "engines": { "node": "^14.17.0 || >=16.0.0" @@ -4262,10 +4264,11 @@ } }, "node_modules/factory.ts": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/factory.ts/-/factory.ts-1.4.1.tgz", - "integrity": "sha512-x5hrzGOZvQnw82ZK+fUo/p1nlbJGCi564FBx3jQWQix6xyEK8xvdCwjdgdmbaUiqfURWWfjgTJyBU5OSfs52tw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/factory.ts/-/factory.ts-1.4.2.tgz", + "integrity": "sha512-8x2hqK1+EGkja4Ah8H3nkP7rDUJsBK1N3iFDqzqsaOV114o2IphSdVkFIw9nDHHr37gFFy2NXeN6n10ieqHzZg==", "dev": true, + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "source-map-support": "^0.5.21" @@ -4813,15 +4816,15 @@ } }, "node_modules/intl-messageformat": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.6.0.tgz", - "integrity": "sha512-AYKl/DY1nl75pJU8EK681JOVL40uQTNJe3yEMXKfydDFoz+5hNrM/PqjchueSMKGKCZKBVgeexqZwy3uC2B36Q==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.0.tgz", + "integrity": "sha512-2P06M9jFTqJnEQzE072VGPjbAx6ZG1YysgopAwc8ui0ajSjtwX1MeQ6bXFXIzKcNENJTizKkcJIcZ0zlpl1zSg==", "license": "BSD-3-Clause", "dependencies": { - "@formatjs/ecma402-abstract": "2.1.0", - "@formatjs/fast-memoize": "2.2.0", - "@formatjs/icu-messageformat-parser": "2.7.9", - "tslib": "^2.4.0" + "@formatjs/ecma402-abstract": "2.2.0", + "@formatjs/fast-memoize": "2.2.1", + "@formatjs/icu-messageformat-parser": "2.7.10", + "tslib": "^2.7.0" } }, "node_modules/is-arrayish": { @@ -6842,17 +6845,18 @@ "dev": true }, "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", + "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", "dev": true, + "license": "MIT", "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/socket.io-client": { @@ -7197,9 +7201,9 @@ } }, "node_modules/svelte-check": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.0.4.tgz", - "integrity": "sha512-AcHWIPuZb1mh/jKoIrww0ebBPpAvwWd1bfXCnwC2dx4OkydNMaiG//+Xnry91RJMHFH7CiE+6Y2p332DRIaOXQ==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.0.5.tgz", + "integrity": "sha512-icBTBZ3ibBaywbXUat3cK6hB5Du+Kq9Z8CRuyLmm64XIe2/r+lQcbuBx/IQgsbrC+kT2jQ0weVpZSSRIPwB6jQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7237,9 +7241,9 @@ } }, "node_modules/svelte-check/node_modules/fdir": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.0.tgz", - "integrity": "sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -7281,9 +7285,9 @@ } }, "node_modules/svelte-eslint-parser": { - "version": "0.41.1", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.1.tgz", - "integrity": "sha512-08ndI6zTghzI8SuJAFpvMbA/haPSGn3xz19pjre19yYMw8Nw/wQJ2PrZBI/L8ijGTgtkWCQQiLLy+Z1tfaCwNA==", + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.42.0.tgz", + "integrity": "sha512-e7LyqFPTuF43ZYhKOf0Gq1lzP+G64iWVJXAIcwVxohGx5FFyqdUkw7DEXNjZ+Fm+TAA98zPmDqWvgD1OpyMi5A==", "dev": true, "license": "MIT", "dependencies": { @@ -7779,15 +7783,16 @@ } }, "node_modules/svelte-parse-markup": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/svelte-parse-markup/-/svelte-parse-markup-0.1.2.tgz", - "integrity": "sha512-DycY7DJr7VqofiJ63ut1/NEG92HrWWL56VWITn/cJCu+LlZhMoBkBXT4opUitPEEwbq1nMQbv4vTKUfbOqIW1g==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/svelte-parse-markup/-/svelte-parse-markup-0.1.5.tgz", + "integrity": "sha512-T6mqZrySltPCDwfKXWQ6zehipVLk4GWfH1zCMGgRtLlOIFPuw58ZxVYxVvotMJgJaurKi1i14viB2GIRKXeJTQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://bjornlu.com/sponsor" }, "peerDependencies": { - "svelte": "^3.0.0 || ^4.0.0" + "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1" } }, "node_modules/symbol-tree": { @@ -7799,9 +7804,9 @@ "peer": true }, "node_modules/tailwindcss": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", - "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "version": "3.4.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", + "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", "dev": true, "license": "MIT", "dependencies": { @@ -7886,9 +7891,9 @@ } }, "node_modules/tailwindcss/node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", "dev": true, "license": "ISC", "bin": { @@ -7984,9 +7989,9 @@ } }, "node_modules/three": { - "version": "0.168.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.168.0.tgz", - "integrity": "sha512-6m6jXtDwMJEK/GGMbAOTSAmxNdzKvvBzgd7q8bE/7Tr6m7PaBh5kKLrN7faWtlglXbzj7sVba48Idwx+NRsZXw==", + "version": "0.169.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.169.0.tgz", + "integrity": "sha512-Ed906MA3dR4TS5riErd4QBsRGPcx+HBDX2O5yYE5GqJeFQTPU+M56Va/f/Oph9X7uZo3W3o4l2ZhBZ6f6qUv0w==", "license": "MIT" }, "node_modules/thumbhash": { @@ -8139,9 +8144,9 @@ "dev": true }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", "license": "0BSD" }, "node_modules/type": { @@ -8294,9 +8299,9 @@ } }, "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz", + "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==", "dev": true, "license": "MIT", "dependencies": { @@ -8367,9 +8372,9 @@ } }, "node_modules/vite-node": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.2.tgz", - "integrity": "sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz", + "integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==", "dev": true, "license": "MIT", "dependencies": { @@ -8403,19 +8408,19 @@ } }, "node_modules/vitest": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.2.tgz", - "integrity": "sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz", + "integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.2", - "@vitest/mocker": "2.1.2", - "@vitest/pretty-format": "^2.1.2", - "@vitest/runner": "2.1.2", - "@vitest/snapshot": "2.1.2", - "@vitest/spy": "2.1.2", - "@vitest/utils": "2.1.2", + "@vitest/expect": "2.1.3", + "@vitest/mocker": "2.1.3", + "@vitest/pretty-format": "^2.1.3", + "@vitest/runner": "2.1.3", + "@vitest/snapshot": "2.1.3", + "@vitest/spy": "2.1.3", + "@vitest/utils": "2.1.3", "chai": "^5.1.1", "debug": "^4.3.6", "magic-string": "^0.30.11", @@ -8426,7 +8431,7 @@ "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.2", + "vite-node": "2.1.3", "why-is-node-running": "^2.3.0" }, "bin": { @@ -8441,8 +8446,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.2", - "@vitest/ui": "2.1.2", + "@vitest/browser": "2.1.3", + "@vitest/ui": "2.1.3", "happy-dom": "*", "jsdom": "*" }, diff --git a/web/src/app.d.ts b/web/src/app.d.ts index b13a0c97d5..ccec3f33d6 100644 --- a/web/src/app.d.ts +++ b/web/src/app.d.ts @@ -28,7 +28,7 @@ interface Element { requestFullscreen?(options?: FullscreenOptions): Promise; } -import type en from '$lib/i18n/en.json'; +import type en from '$lib/en.json'; import 'svelte-i18n'; type NestedKeys = K extends keyof T & string diff --git a/web/src/lib/components/album-page/__tests__/album-card.spec.ts b/web/src/lib/components/album-page/__tests__/album-card.spec.ts index 79136bca02..8da9fbfd45 100644 --- a/web/src/lib/components/album-page/__tests__/album-card.spec.ts +++ b/web/src/lib/components/album-page/__tests__/album-card.spec.ts @@ -12,7 +12,7 @@ describe('AlbumCard component', () => { beforeAll(async () => { await init({ fallbackLocale: 'en-US' }); - register('en-US', () => import('$lib/i18n/en.json')); + register('en-US', () => import('$i18n/en.json')); await waitLocale('en-US'); }); diff --git a/web/src/lib/components/album-page/album-options.svelte b/web/src/lib/components/album-page/album-options.svelte index 53fd355c4b..3ec1842757 100644 --- a/web/src/lib/components/album-page/album-options.svelte +++ b/web/src/lib/components/album-page/album-options.svelte @@ -6,6 +6,8 @@ type AlbumResponseDto, type UserResponseDto, AssetOrder, + AlbumUserRole, + updateAlbumUser, } from '@immich/sdk'; import { mdiArrowDownThin, mdiArrowUpThin, mdiPlus, mdiDotsVertical } from '@mdi/js'; import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; @@ -29,6 +31,7 @@ export let onToggleEnabledActivity: () => void; export let onShowSelectSharedUser: () => void; export let onRemove: (userId: string) => void; + export let onRefreshAlbum: () => void; let selectedRemoveUser: UserResponseDto | null = null; @@ -80,6 +83,21 @@ selectedRemoveUser = null; } }; + + const handleUpdateSharedUserRole = async (user: UserResponseDto, role: AlbumUserRole) => { + try { + await updateAlbumUser({ id: album.id, userId: user.id, updateAlbumUserDto: { role } }); + const message = $t('user_role_set', { + values: { user: user.name, role: role == AlbumUserRole.Viewer ? $t('role_viewer') : $t('role_editor') }, + }); + onRefreshAlbum(); + notificationController.show({ type: NotificationType.Info, message }); + } catch (error) { + handleError(error, $t('errors.unable_to_change_album_user_role')); + } finally { + selectedRemoveUser = null; + } + }; {#if !selectedRemoveUser} @@ -122,15 +140,31 @@
{$t('owner')}
- {#each album.albumUsers as { user } (user.id)} + {#each album.albumUsers as { user, role } (user.id)}
{user.name}
+ {#if role === AlbumUserRole.Viewer} + {$t('role_viewer')} + {:else} + {$t('role_editor')} + {/if} {#if user.id !== album.ownerId} - + {#if role === AlbumUserRole.Viewer} + handleUpdateSharedUserRole(user, AlbumUserRole.Editor)} + text={$t('allow_edits')} + /> + {:else} + handleUpdateSharedUserRole(user, AlbumUserRole.Viewer)} + text={$t('disallow_edits')} + /> + {/if} + handleMenuRemove(user)} text={$t('remove')} /> {/if} diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte index 2256c79eb0..87b3d8e2c5 100644 --- a/web/src/lib/components/album-page/album-viewer.svelte +++ b/web/src/lib/components/album-page/album-viewer.svelte @@ -6,7 +6,7 @@ import type { AlbumResponseDto, SharedLinkResponseDto, UserResponseDto } from '@immich/sdk'; import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store'; import { AssetStore } from '$lib/stores/assets.store'; - import { downloadAlbum } from '$lib/utils/asset-utils'; + import { cancelMultiselect, downloadAlbum } from '$lib/utils/asset-utils'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; import DownloadAction from '../photos-page/actions/download-action.svelte'; import AssetGrid from '../photos-page/asset-grid.svelte'; @@ -49,7 +49,7 @@ shortcut: { key: 'Escape' }, onShortcut: () => { if (!$showAssetViewer && $isMultiSelectState) { - assetInteractionStore.clearMultiselect(); + cancelMultiselect(assetInteractionStore); } }, }} diff --git a/web/src/lib/components/faces-page/manage-people-visibility.svelte b/web/src/lib/components/faces-page/manage-people-visibility.svelte index 23a69e7759..a48fd6bf74 100644 --- a/web/src/lib/components/faces-page/manage-people-visibility.svelte +++ b/web/src/lib/components/faces-page/manage-people-visibility.svelte @@ -145,7 +145,7 @@ {@const hidden = personIsHidden[person.id]}